diff --git a/src/actions/heal.cpp b/src/actions/heal.cpp index a90ac892ffc6..b4ee998c2faf 100644 --- a/src/actions/heal.cpp +++ b/src/actions/heal.cpp @@ -24,7 +24,7 @@ #include "../gettext.hpp" #include "../log.hpp" #include "../map.hpp" -#include "../replay.hpp" +#include "../play_controller.hpp" #include "../resources.hpp" #include "../team.hpp" #include "../unit.hpp" @@ -352,7 +352,7 @@ void calculate_healing(int side, bool update_display) const team & viewing_team = (*resources::teams)[resources::screen->viewing_team()]; - if (!recorder.is_skipping() && update_display && + if (!resources::controller->is_skipping_replay() && update_display && patient.is_visible_to_team(viewing_team, resources::gameboard->map(), false) ) { unit_list.push_front(heal_unit(patient, healers, healing, curing == POISON_CURE)); diff --git a/src/ai/actions.cpp b/src/ai/actions.cpp index 0fe8b597b670..bf4045bbc3b8 100644 --- a/src/ai/actions.cpp +++ b/src/ai/actions.cpp @@ -44,6 +44,7 @@ #include "../mouse_handler_base.hpp" #include "../pathfind/teleport.hpp" #include "../play_controller.hpp" +#include "../playsingle_controller.hpp" #include "../recall_list_manager.hpp" #include "../replay_helper.hpp" #include "../resources.hpp" @@ -104,6 +105,12 @@ void action_result::execute() if(resources::controller->is_end_turn()) { throw ai_end_turn_exception(); } + + if(playsingle_controller* psc = dynamic_cast(resources::controller)) { + if(psc->get_player_type_changed()) { + throw restart_turn_exception(); + } + } is_execution_ = false; } diff --git a/src/ai/manager.cpp b/src/ai/manager.cpp index 1010c69c37e5..77336605909f 100644 --- a/src/ai/manager.cpp +++ b/src/ai/manager.cpp @@ -798,6 +798,8 @@ void manager::play_turn( side_number side ){ } catch (const ai_end_turn_exception&) { } + catch(const restart_turn_exception&) { + } const int turn_end_time= SDL_GetTicks(); DBG_AI_MANAGER << "side " << side << ": number of user interactions: "< #include #include -#include MAKE_ENUM(LEVEL_RESULT, - (NONE, "none") (VICTORY, "victory") (DEFEAT, "defeat") (QUIT, "quit") (OBSERVER_END, "observer_end") - (SKIP_TO_LINGER,"skip_to_linger") ) MAKE_ENUM_STREAM_OPS1(LEVEL_RESULT) @@ -61,13 +58,6 @@ class ai_end_turn_exception IMPLEMENT_LUA_JAILBREAK_EXCEPTION(ai_end_turn_exception) }; - -/** - * Struct used to transmit info caught from an end_turn_exception. - */ -struct restart_turn_struct { -}; - /** * Exception used to signal the end of a player turn. */ @@ -82,10 +72,6 @@ class restart_turn_exception { } const char * what() const throw() { return "restart_turn_exception"; } - restart_turn_struct to_struct() - { - return restart_turn_struct(); - } private: @@ -96,7 +82,7 @@ class restart_turn_exception * Struct used to transmit info caught from an end_turn_exception. */ struct end_level_struct { - LEVEL_RESULT result; + bool is_quit; }; /** @@ -108,17 +94,17 @@ class end_level_exception { public: - end_level_exception(LEVEL_RESULT res) + end_level_exception(bool isquit = false) : tlua_jailbreak_exception() , std::exception() - , result(res) + , is_quit(isquit) { } - LEVEL_RESULT result; + bool is_quit; end_level_struct to_struct() { - end_level_struct els = {result}; + end_level_struct els = {is_quit}; return els; } @@ -131,7 +117,7 @@ class end_level_exception /** * The two end_*_exceptions are caught and transformed to this signaling object */ -typedef boost::optional > possible_end_play_signal; +typedef boost::optional possible_end_play_signal; #define HANDLE_END_PLAY_SIGNAL( X )\ do\ @@ -140,8 +126,6 @@ do\ X;\ } catch (end_level_exception & e) {\ return possible_end_play_signal(e.to_struct());\ - } catch (restart_turn_exception & e) {\ - return possible_end_play_signal(e.to_struct());\ }\ }\ while(0) @@ -173,35 +157,6 @@ do\ while(0) -enum END_PLAY_SIGNAL_TYPE {END_TURN, END_LEVEL}; - -class get_signal_type : public boost::static_visitor { -public: - END_PLAY_SIGNAL_TYPE operator()(restart_turn_struct &) const - { - return END_TURN; - } - - END_PLAY_SIGNAL_TYPE operator()(end_level_struct& ) const - { - return END_LEVEL; - } -}; - -class get_result : public boost::static_visitor { -public: - LEVEL_RESULT operator()(restart_turn_struct & ) const - { - return NONE; - } - - LEVEL_RESULT operator()(end_level_struct & s) const - { - return s.result; - } -}; - - /** * The non-persistent part of end_level_data */ @@ -213,7 +168,6 @@ struct transient_end_level{ bool linger_mode; /**< Should linger mode be invoked? */ std::string custom_endlevel_music; /**< Custom short music played at the end. */ bool reveal_map; /**< Should we reveal map when game is ended? (Multiplayer only) */ - bool disabled; /**< Limits execution of tag [endlevel] to a single time > */ }; /** @@ -228,7 +182,7 @@ struct end_level_data bool replay_save; /**< Should a replay save be made? */ bool proceed_to_next_level; /**< whether to proceed to the next scenario, equals (res == VICTORY) in sp. We need to save this in saves during linger mode. > */ transient_end_level transient; - + bool is_victory; void write(config& cfg) const; void read(const config& cfg); @@ -240,5 +194,11 @@ struct end_level_data return r; } }; - +inline void throw_quit_game_exception() +{ + // Distinguish 'Quit' from 'Regular' end_level_exceptions to solve the following problem: + // If a player quits the game during an event after an [endlevel] occurs, the game won't + // Quit but continue with the [endlevel] instead. + throw end_level_exception(true); +} #endif /* ! GAME_END_EXCEPTIONS_HPP_INCLUDED */ diff --git a/src/game_events/action_wml.cpp b/src/game_events/action_wml.cpp index bdee1065b909..b73483f0d5e5 100644 --- a/src/game_events/action_wml.cpp +++ b/src/game_events/action_wml.cpp @@ -635,7 +635,7 @@ WML_HANDLER_FUNCTION(message, event_info, cfg) } has_input = !options.empty() || has_text_input; - if (!has_input && get_replay_source().is_skipping()) { + if (!has_input && resources::controller->is_skipping_replay()) { // No input to get and the user is not interested either. return; } diff --git a/src/game_initialization/playcampaign.cpp b/src/game_initialization/playcampaign.cpp index c43895112148..a88ffe2b3252 100644 --- a/src/game_initialization/playcampaign.cpp +++ b/src/game_initialization/playcampaign.cpp @@ -171,7 +171,8 @@ LEVEL_RESULT play_replay(display& disp, saved_game& gamestate, const config& gam e.show(disp); } } - return NONE; + //TODO: when can this happen? + return VICTORY; } static LEVEL_RESULT playsingle_scenario(const config& game_config, @@ -188,28 +189,23 @@ static LEVEL_RESULT playsingle_scenario(const config& game_config, LEVEL_RESULT res = playcontroller.play_scenario(story, skip_replay); + if (res == QUIT) + { + return QUIT; + } + end_level = playcontroller.get_end_level_data_const(); - if (res != QUIT) + show_carryover_message(state_of_game, playcontroller, disp, end_level, res); + if(!disp.video().faked()) { - //if we are loading from linger mode then we already did this. - if(res != SKIP_TO_LINGER) - { - show_carryover_message(state_of_game, playcontroller, disp, end_level, res); - } - if(!disp.video().faked()) - { - try { - playcontroller.maybe_linger(); - } catch(end_level_exception& e) { - if (e.result == QUIT) { - return QUIT; - } - } + try { + playcontroller.maybe_linger(); + } catch(end_level_exception&) { + return QUIT; } } state_of_game.set_snapshot(playcontroller.to_config()); - return res; } @@ -226,31 +222,30 @@ static LEVEL_RESULT playmp_scenario(const config& game_config, game_config, tdata, disp.video(), skip_replay, blindfold_replay, io_type == IO_SERVER); LEVEL_RESULT res = playcontroller.play_scenario(story, skip_replay); - end_level = playcontroller.get_end_level_data_const(); - //Check if the player started as mp client and changed to host if (io_type == IO_CLIENT && playcontroller.is_host()) io_type = IO_SERVER; - if (res != QUIT) + if (res == QUIT) { - if(res != OBSERVER_END && res != SKIP_TO_LINGER) - { - //We need to call this before linger because it prints the defeated/victory message. - //(we want to see that message before entering the linger mode) - show_carryover_message(state_of_game, playcontroller, disp, end_level, res); - } - if(!disp.video().faked()) - { - try { - playcontroller.maybe_linger(); - } catch(end_level_exception& e) { - if (e.result == QUIT) { - return QUIT; - } - } - } + return QUIT; + } + + end_level = playcontroller.get_end_level_data_const(); + if(res != OBSERVER_END) + { + //We need to call this before linger because it prints the defeated/victory message. + //(we want to see that message before entering the linger mode) + show_carryover_message(state_of_game, playcontroller, disp, end_level, res); + } + if(!disp.video().faked()) + { + try { + playcontroller.maybe_linger(); + } catch(end_level_exception&) { + return QUIT; + } } playcontroller.update_savegame_snapshot(); return res; @@ -317,40 +312,35 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, if (is_unit_test) { return res; } - //in this case we might have skipped state.set_snapshot which means we cannot do gamestate.convert_to_start_save(); - if(res == QUIT) - { + if(res == QUIT) { return res; } - - // Save-management options fire on game end. - // This means: (a) we have a victory, or - // (b) we're multiplayer live, in which - // case defeat is also game end. Someday, - // if MP campaigns ever work again, we might - // need to change this test. - if (res == VICTORY || (game_type == game_classification::MULTIPLAYER && res == DEFEAT)) { - if (preferences::delete_saves()) - savegame::clean_saves(gamestate.classification().label); - - if (preferences::save_replays() && end_level.replay_save) { - savegame::replay_savegame save(gamestate, preferences::save_compression_format()); - save.save_game_automatic(disp.video(), true); - } + // proceed_to_next_level <=> 'any human side recieved victory' + // If 'any human side recieved victory' we do the Save-management options + // Otherwise we are done now + if(!end_level.proceed_to_next_level) { + return res; } + if (preferences::delete_saves()) { + savegame::clean_saves(gamestate.classification().label); + } + if (preferences::save_replays() && end_level.replay_save) { + savegame::replay_savegame save(gamestate, preferences::save_compression_format()); + save.save_game_automatic(disp.video(), true); + } + gamestate.convert_to_start_save(); recorder.clear(); - // On DEFEAT, QUIT, or OBSERVER_END, we're done now - //If there is no next scenario we're done now. - if(!end_level.proceed_to_next_level || gamestate.get_scenario_id().empty()) + if(gamestate.get_scenario_id().empty()) { return res; } else if(res == OBSERVER_END) { + // TODO: does it make sense to ask this question if we are currently the host? const int dlg_res = gui2::show_message(disp.video(), _("Game Over"), _("This scenario has ended. Do you want to continue the campaign?"), gui2::tmessage::yes_no_buttons); diff --git a/src/game_launcher.cpp b/src/game_launcher.cpp index 6b0dea768007..9d8124effb6f 100644 --- a/src/game_launcher.cpp +++ b/src/game_launcher.cpp @@ -593,7 +593,7 @@ int game_launcher::unit_test() try { LEVEL_RESULT res = play_game(disp(),state_,game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types(), IO_SERVER, false, false, false, true); - if (!(res == VICTORY || res == NONE) || lg::broke_strict()) { + if (!(res == VICTORY) || lg::broke_strict()) { return 1; } } catch (game::load_game_exception &) { @@ -629,12 +629,10 @@ int game_launcher::unit_test() try { //LEVEL_RESULT res = play_game(disp(), state_, game_config_manager::get()->game_config(), IO_SERVER, false,false,false,true); LEVEL_RESULT res = ::play_replay(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types(), true); - if (!(res == VICTORY || res == NONE)) { + if (!(res == VICTORY)) { std::cerr << "Observed failure on replay" << std::endl; return 4; } - /*::play_replay(disp(),state_,game_config_manager::get()->game_config(), - video_);*/ clear_loaded_game(); } catch (game::load_game_exception &) { std::cerr << "Load_game_exception encountered during play_replay!" << std::endl; diff --git a/src/hotkey/command_executor.cpp b/src/hotkey/command_executor.cpp index 28af202ee5c7..1166cb598baf 100644 --- a/src/hotkey/command_executor.cpp +++ b/src/hotkey/command_executor.cpp @@ -522,7 +522,7 @@ void key_event(display& disp, const SDL_KeyboardEvent& event, command_executor* const int res = gui2::show_message(disp.video(), _("Quit"), _("Do you really want to quit?"), gui2::tmessage::yes_no_buttons); if(res != gui2::twindow::CANCEL) { - throw end_level_exception(QUIT); + throw_quit_game_exception(); } else { return; } @@ -693,7 +693,7 @@ void execute_command(display& disp, const hotkey_command& command, command_execu const int res = gui2::show_message(disp.video(), _("Quit"), _("Do you really want to quit?"), gui2::tmessage::yes_no_buttons); if (res != gui2::twindow::CANCEL) { - throw end_level_exception(QUIT); + throw_quit_game_exception(); } } break; diff --git a/src/hotkey_handler_mp.cpp b/src/hotkey_handler_mp.cpp index 61ee68e2fb41..7b310efe11d2 100644 --- a/src/hotkey_handler_mp.cpp +++ b/src/hotkey_handler_mp.cpp @@ -72,11 +72,6 @@ bool playmp_controller::hotkey_handler::can_execute_command(const hotkey::hotkey case hotkey::HOTKEY_STOP_NETWORK: res = is_observer(); break; - case hotkey::HOTKEY_REPLAY_STOP: - if (is_observer()){ - playmp_controller_.stop_network(); - } - break; default: return playsingle_controller::hotkey_handler::can_execute_command(cmd, index); } diff --git a/src/menu_events.cpp b/src/menu_events.cpp index 0f7a221f5295..39c4890c9c9e 100644 --- a/src/menu_events.cpp +++ b/src/menu_events.cpp @@ -61,6 +61,7 @@ #include "menu_events.hpp" #include "mouse_events.hpp" #include "play_controller.hpp" +#include "playsingle_controller.hpp" #include "preferences_display.hpp" #include "replay.hpp" #include "replay_helper.hpp" @@ -2711,9 +2712,9 @@ void console_handler::do_droid() { } menu_handler_.teams()[side - 1].toggle_droid(); if(team_num_ == side) { - //if it is our turn at the moment, we have to indicate to the - //play_controller, that we are no longer in control - throw restart_turn_exception(); + if(playsingle_controller* psc = dynamic_cast(&menu_handler_.pc_)) { + psc->set_player_type_changed(); + } } } else if (menu_handler_.teams()[side - 1].is_local_ai()) { // menu_handler_.teams()[side - 1].make_human(); @@ -2756,9 +2757,9 @@ void console_handler::do_idle() { //toggle the proxy controller between idle / non idle menu_handler_.teams()[side - 1].toggle_idle(); if(team_num_ == side) { - //if it is our turn at the moment, we have to indicate to the - //play_controller, that we are no longer in control - throw restart_turn_exception(); + if(playsingle_controller* psc = dynamic_cast(&menu_handler_.pc_)) { + psc->set_player_type_changed(); + } } } menu_handler_.textbox_info_.close(*menu_handler_.gui_); @@ -2950,7 +2951,7 @@ void console_handler::do_save_quit() { do_quit(); } void console_handler::do_quit() { - throw end_level_exception(QUIT); + throw_quit_game_exception(); } void console_handler::do_ignore_replay_errors() { game_config::ignore_replay_errors = (get_data() != "off") ? true : false; @@ -2963,12 +2964,14 @@ void console_handler::do_next_level() { if (!get_data().empty()) menu_handler_.gamedata().set_next_scenario(get_data()); - end_level_data &e = menu_handler_.pc_.get_end_level_data(); + end_level_data e; e.transient.carryover_report = false; e.prescenario_save = true; e.transient.linger_mode = false; e.proceed_to_next_level = true; - throw end_level_exception(VICTORY); + e.is_victory = true; + menu_handler_.pc_.set_end_level_data(e); + throw end_level_exception(); } void console_handler::do_choose_level() { @@ -3011,12 +3014,14 @@ void console_handler::do_choose_level() { if (size_t(choice) < options.size()) { menu_handler_.gamedata().set_next_scenario(options[choice]); - end_level_data &e = menu_handler_.pc_.get_end_level_data(); + end_level_data e; e.transient.carryover_report = false; e.prescenario_save = true; e.transient.linger_mode = false; e.proceed_to_next_level = true; - throw end_level_exception(VICTORY); + e.is_victory = true; + menu_handler_.pc_.set_end_level_data(e); + throw end_level_exception(); } } diff --git a/src/mouse_events.cpp b/src/mouse_events.cpp index 28e31be4e45f..9878a3e7cdf5 100644 --- a/src/mouse_events.cpp +++ b/src/mouse_events.cpp @@ -67,7 +67,6 @@ #include "SDL_mouse.h" // for SDL_GetMouseState #include "SDL_video.h" // for SDL_Color -class restart_turn_exception; namespace gui { class slider; } static lg::log_domain log_engine("engine"); @@ -889,18 +888,9 @@ size_t mouse_handler::move_unit_along_route(const std::vector & st } } - size_t moves = 0; - try { - - LOG_NG << "move unit along route from " << steps.front() << " to " << steps.back() << "\n"; - moves = actions::move_unit_and_record(steps, &pc_.get_undo_stack(), - false, true, &interrupted); - } catch(restart_turn_exception&) { - cursor::set(cursor::NORMAL); - gui().invalidate_game_status(); - throw; - } - + LOG_NG << "move unit along route from " << steps.front() << " to " << steps.back() << "\n"; + size_t moves = actions::move_unit_and_record(steps, &pc_.get_undo_stack(), false, true, &interrupted); + cursor::set(cursor::NORMAL); gui().invalidate_game_status(); diff --git a/src/play_controller.cpp b/src/play_controller.cpp index 954b62f53400..3c202f22779c 100644 --- a/src/play_controller.cpp +++ b/src/play_controller.cpp @@ -37,6 +37,7 @@ #include "hotkey_handler.hpp" #include "map_label.hpp" #include "gettext.hpp" +#include "gui/dialogs/transient_message.hpp" #include "halo.hpp" #include "hotkey/command_executor.hpp" #include "loadscreen.hpp" @@ -137,7 +138,7 @@ play_controller::play_controller(const config& level, saved_game& state_of_game, , start_turn_(gamestate_.tod_manager_.turn()) // gamestate_.tod_manager_ constructed above , skip_replay_(skip_replay) , linger_(false) - , it_is_a_new_turn_(true) + , it_is_a_new_turn_(level["it_is_a_new_turn"].to_bool(true)) , init_side_done_(level["init_side_done"].to_bool(false)) , ticks_(ticks) , victory_when_enemies_defeated_(true) @@ -162,6 +163,14 @@ play_controller::play_controller(const config& level, saved_game& state_of_game, resources::mp_settings = &saved_game_.mp_settings(); persist_.start_transaction(); + + if(const config& endlevel_cfg = level.child("end_level_data")) { + end_level_data el_data; + el_data.read(endlevel_cfg); + el_data.transient.carryover_report = false; + set_end_level_data(el_data); + } + n_unit::id_manager::instance().set_save_id(level_["next_underlying_unit_id"]); // Setup victory and defeat music @@ -184,7 +193,7 @@ play_controller::~play_controller() hotkey::delete_all_wml_hotkeys(); clear_resources(); } - +struct throw_end_level { void operator()(const config&) { throw_quit_game_exception(); } }; void play_controller::init(CVideo& video){ util::scoped_resource scoped_loadscreen_manager; loadscreen::global_loadscreen_manager* loadscreen_manager = loadscreen::global_loadscreen_manager::get(); @@ -195,7 +204,6 @@ void play_controller::init(CVideo& video){ } loadscreen::start_stage("load level"); - recorder.set_skip(false); LOG_NG << "initializing game_state..." << (SDL_GetTicks() - ticks_) << std::endl; gamestate_.init(ticks_, *this); @@ -205,9 +213,6 @@ void play_controller::init(CVideo& video){ whiteboard_manager_.reset(new wb::manager()); resources::whiteboard = whiteboard_manager_; - // mouse_handler expects at least one team for linger mode to work. - if (gamestate_.board_.teams().empty()) end_level_data_.transient.linger_mode = false; - LOG_NG << "loading units..." << (SDL_GetTicks() - ticks_) << std::endl; loadscreen::start_stage("load units"); preferences::encounter_all_content(gamestate_.board_); @@ -267,7 +272,7 @@ void play_controller::init(CVideo& video){ plugins_context_.reset(new plugins_context("Game")); plugins_context_->set_callback("save_game", boost::bind(&play_controller::save_game_auto, this, boost::bind(get_str, _1, "filename" )), true); plugins_context_->set_callback("save_replay", boost::bind(&play_controller::save_replay_auto, this, boost::bind(get_str, _1, "filename" )), true); - plugins_context_->set_callback("quit", boost::bind(&play_controller::force_end_level, this, QUIT), false); + plugins_context_->set_callback("quit", throw_end_level(), false); } void play_controller::init_managers(){ @@ -301,38 +306,22 @@ void play_controller::fire_prestart() gamestate_.gamedata_.get_variable("turn_number") = int(start_turn_); } -void play_controller::fire_start(bool execute){ - if(execute) { - gamestate_.gamedata_.set_phase(game_data::START); - pump().fire("start"); - check_end_level(); - // start event may modify start turn with WML, reflect any changes. - start_turn_ = turn(); - gamestate_.gamedata_.get_variable("turn_number") = int(start_turn_); - - // prestart and start events may modify the initial gold amount, - // reflect any changes. - BOOST_FOREACH(team& tm, gamestate_.board_.teams_) - { - tm.set_start_gold(tm.gold()); - } - init_side_done_ = false; +void play_controller::fire_start() +{ + gamestate_.gamedata_.set_phase(game_data::START); + pump().fire("start"); + check_end_level(); + // start event may modify start turn with WML, reflect any changes. + start_turn_ = turn(); + gamestate_.gamedata_.get_variable("turn_number") = int(start_turn_); - } else { - it_is_a_new_turn_ = false; - } - if( saved_game_.classification().random_mode != "" && (network::nconnections() != 0)) + // prestart and start events may modify the initial gold amount, + // reflect any changes. + BOOST_FOREACH(team& tm, gamestate_.board_.teams_) { - std::string mes = _("MP game uses an alternative random mode, if you don't know what this message means, then most likeley someone is cheating or someone reloaded a corrupt game."); - gui_->get_chat_manager().add_chat_message( - time(NULL), - "game_engine", - 0, - mes, - events::chat_handler::MESSAGE_PUBLIC, - preferences::message_bell()); - replay::process_error(mes); + tm.set_start_gold(tm.gold()); } + init_side_done_ = false; gamestate_.gamedata_.set_phase(game_data::PLAY); } @@ -424,7 +413,7 @@ void play_controller::do_init_side() current_team().spend_gold(expense); } - calculate_healing(player_number_, !skip_replay_); + calculate_healing(player_number_, !is_skipping_replay()); } // Prepare the undo stack. @@ -448,11 +437,11 @@ void play_controller::init_side_end() if (player_number_ == first_player_) sound::play_sound(tod.sounds, sound::SOUND_SOURCES); - if (!recorder.is_skipping()){ + if (!is_skipping_replay()){ gui_->invalidate_all(); } - if (!recorder.is_skipping() && !skip_replay_ && current_team().get_scroll_to_leader()){ + if (!is_skipping_replay() && current_team().get_scroll_to_leader()){ gui_->scroll_to_leader(player_number_,game_display::ONSCREEN,false); } whiteboard_manager_->on_init_side(); @@ -464,11 +453,12 @@ config play_controller::to_config() const cfg.merge_attributes(level_); cfg["init_side_done"] = init_side_done_; + cfg["it_is_a_new_turn"] = it_is_a_new_turn_; gamestate_.write(cfg); - if(linger_) { - end_level_data_.write(cfg.add_child("end_level_data")); + if(end_level_data_.get_ptr() != NULL) { + end_level_data_->write(cfg.add_child("end_level_data")); } // Write terrain_graphics data in snapshot, too @@ -836,7 +826,6 @@ const std::string& play_controller::select_defeat_music() const return defeat_music_[rand() % defeat_music_.size()]; } - void play_controller::set_victory_music_list(const std::string& list) { victory_music_ = utils::split(list); @@ -900,8 +889,11 @@ void play_controller::check_victory() DBG_EE << "throwing end level exception..." << std::endl; //Also proceed to the next scenario when another player survived. - end_level_data_.proceed_to_next_level = found_player || found_network_player; - throw end_level_exception(found_player ? VICTORY : DEFEAT); + end_level_data el_data; + el_data.proceed_to_next_level = found_player || found_network_player; + el_data.is_victory = found_player; + set_end_level_data(el_data); + throw end_level_exception(); } void play_controller::process_oos(const std::string& msg) const diff --git a/src/play_controller.hpp b/src/play_controller.hpp index bdacb9d34ad6..ffdb0a7dc508 100644 --- a/src/play_controller.hpp +++ b/src/play_controller.hpp @@ -111,7 +111,6 @@ class play_controller : public controller_base, public events::observer, public void init_side_end(); virtual void force_end_turn() = 0; - virtual void force_end_level(LEVEL_RESULT res) = 0; virtual void check_end_level() = 0; virtual void on_not_observer() = 0; @@ -125,11 +124,18 @@ class play_controller : public controller_base, public events::observer, public { victory_when_enemies_defeated_ = e; } void set_remove_from_carryover_on_defeat(bool e) { remove_from_carryover_on_defeat_= e; } - end_level_data& get_end_level_data() { - return end_level_data_; + + void set_end_level_data(const end_level_data& data) { + end_level_data_ = data; + } + void reset_end_level_data() { + end_level_data_ = boost::none_t(); + } + bool is_regular_game_end() const { + return end_level_data_.get_ptr() != NULL; } const end_level_data& get_end_level_data_const() const { - return end_level_data_; + return *end_level_data_; } const std::vector& get_teams_const() const { return gamestate_.board_.teams_; @@ -215,7 +221,7 @@ class play_controller : public controller_base, public events::observer, public ///preload events cannot be synced void fire_preload(); void fire_prestart(); - void fire_start(bool execute); + void fire_start(); virtual void init_gui(); virtual void finish_side_turn(); void finish_turn(); //this should not throw an end turn or end level exception @@ -279,7 +285,6 @@ class play_controller : public controller_base, public events::observer, public const std::string& select_victory_music() const; const std::string& select_defeat_music() const; - void set_victory_music_list(const std::string& list); void set_defeat_music_list(const std::string& list); @@ -294,7 +299,8 @@ class play_controller : public controller_base, public events::observer, public bool victory_when_enemies_defeated_; bool remove_from_carryover_on_defeat_; - end_level_data end_level_data_; + typedef boost::optional t_possible_end_level_data; + t_possible_end_level_data end_level_data_; std::vector victory_music_; std::vector defeat_music_; diff --git a/src/playmp_controller.cpp b/src/playmp_controller.cpp index 84224a7588b6..fd53c1abe03f 100644 --- a/src/playmp_controller.cpp +++ b/src/playmp_controller.cpp @@ -93,12 +93,6 @@ possible_end_play_signal playmp_controller::play_side() return playsingle_controller::play_side(); } -void playmp_controller::before_human_turn(){ - LOG_NG << "playmp::before_human_turn...\n"; - playsingle_controller::before_human_turn(); - turn_data_.send_data(); -} - void playmp_controller::on_not_observer() { remove_blindfold(); } @@ -134,15 +128,16 @@ possible_end_play_signal playmp_controller::play_human_turn() if (!linger_ || is_host()) { end_turn_enable(true); } - while(!end_turn_) { + while(!end_turn_ && !player_type_changed_) { try { config cfg; if(network_reader_.read(cfg)) { turn_info::PROCESS_DATA_RESULT res; - HANDLE_END_PLAY_SIGNAL( res = turn_data_.process_network_data(cfg, skip_replay_) ); + HANDLE_END_PLAY_SIGNAL( res = turn_data_.process_network_data(cfg) ); if (res == turn_info::PROCESS_RESTART_TURN) { + player_type_changed_ = true; // Clean undo stack if turn has to be restarted (losing control) if ( undo_stack_->can_undo() ) { @@ -161,7 +156,7 @@ possible_end_play_signal playmp_controller::play_human_turn() while( undo_stack_->can_undo() ) undo_stack_->undo(); - return possible_end_play_signal(restart_turn_struct()); + return boost::none; } else if(res == turn_info::PROCESS_END_LINGER) { @@ -205,18 +200,19 @@ possible_end_play_signal playmp_controller::play_idle_loop() remove_blindfold(); - while (!end_turn_) + while (!end_turn_ && !player_type_changed_) { try { config cfg; if(network_reader_.read(cfg)) { turn_info::PROCESS_DATA_RESULT res; - HANDLE_END_PLAY_SIGNAL( res = turn_data_.process_network_data(cfg, skip_replay_) ); + HANDLE_END_PLAY_SIGNAL( res = turn_data_.process_network_data(cfg) ); if (res == turn_info::PROCESS_RESTART_TURN) { - return possible_end_play_signal(restart_turn_struct()); + player_type_changed_ = true; + return boost::none; } } @@ -274,7 +270,7 @@ void playmp_controller::linger() gamestate_.board_.set_all_units_user_end_turn(); set_end_scenario_button(); - + assert(is_regular_game_end()); if ( get_end_level_data_const().transient.reveal_map ) { // Change the view of all players and observers // to see the whole map regardless of shroud and fog. @@ -301,12 +297,6 @@ void playmp_controller::linger() LOG_NG << "caught end-level-exception" << std::endl; reset_end_scenario_button(); throw; - } catch (restart_turn_exception&) { - // thrown if the host leaves the game (sends [leave_game]), we need - // to stay in this loop to stay in linger mode, otherwise the game - // gets aborted - LOG_NG << "caught end-turn-exception" << std::endl; - quit = false; } catch (network::error&) { LOG_NG << "caught network-error-exception" << std::endl; quit = false; @@ -332,13 +322,13 @@ void playmp_controller::wait_for_upload() *gui_, _("Waiting for next scenario..."), cfg); if(res != network::null_connection) { - if (turn_data_.process_network_data_from_reader(skip_replay_) == turn_info::PROCESS_END_LINGER) { + if (turn_data_.process_network_data_from_reader() == turn_info::PROCESS_END_LINGER) { break; } } else { - throw end_level_exception(QUIT); + throw_quit_game_exception(); } } catch(const end_level_exception&) { @@ -388,12 +378,10 @@ possible_end_play_signal playmp_controller::play_network_turn(){ config cfg; if(network_reader_.read(cfg)) { if (replay_last_turn_ <= turn()){ - if (skip_replay_) { - skip_replay_ = false; - } + skip_replay_ = false; } turn_info::PROCESS_DATA_RESULT result; - HANDLE_END_PLAY_SIGNAL ( result = turn_data_.process_network_data(cfg, skip_replay_) ); + HANDLE_END_PLAY_SIGNAL ( result = turn_data_.process_network_data(cfg) ); if(player_type_changed_ == true) { //we received a player change/quit during waiting in get_user_choice/synced_context::pull_remote_user_input @@ -411,13 +399,10 @@ possible_end_play_signal playmp_controller::play_network_turn(){ */ else if(!recorder.at_end()) { - bool was_skipping = recorder.is_skipping(); - recorder.set_skip(skip_replay_); if(do_replay() == REPLAY_FOUND_END_TURN) { break; } - recorder.set_skip(was_skipping); } } @@ -488,8 +473,9 @@ void playmp_controller::do_idle_notification() void playmp_controller::maybe_linger() { - linger_ = true; - if (!get_end_level_data_const().transient.linger_mode) { + // mouse_handler expects at least one team for linger mode to work. + assert(is_regular_game_end()); + if (!get_end_level_data_const().transient.linger_mode || gamestate_.board_.teams().empty()) { if(!is_host()) { // If we continue without lingering we need to // make sure the host uploads the next scenario diff --git a/src/playmp_controller.hpp b/src/playmp_controller.hpp index c101087ec737..d4f6d13225c5 100644 --- a/src/playmp_controller.hpp +++ b/src/playmp_controller.hpp @@ -47,7 +47,6 @@ class playmp_controller : public playsingle_controller, public syncmp_handler void stop_network(); virtual possible_end_play_signal play_side(); - virtual void before_human_turn(); virtual possible_end_play_signal play_human_turn(); virtual void after_human_turn(); virtual void finish_side_turn(); diff --git a/src/playsingle_controller.cpp b/src/playsingle_controller.cpp index c81317633a44..8a9722dd57e3 100644 --- a/src/playsingle_controller.cpp +++ b/src/playsingle_controller.cpp @@ -83,25 +83,34 @@ playsingle_controller::playsingle_controller(const config& level, , replaying_(false) , skip_next_turn_(false) , do_autosaves_(false) - , level_result_(NONE) { hotkey_handler_.reset(new hotkey_handler(*this, saved_game_)); //upgrade hotkey handler to the sp (whiteboard enabled) version + // game may need to start in linger mode - if (state_of_game.classification().completion == "victory" || state_of_game.classification().completion == "defeat") - { - LOG_NG << "Setting linger mode.\n"; - linger_ = true; - } + linger_ = this->is_regular_game_end(); ai::game_info ai_info; ai::manager::set_ai_info(ai_info); ai::manager::add_observer(this) ; - plugins_context_->set_accessor_string("level_result", boost::bind(&LEVEL_RESULT_to_string, level_result_)); + plugins_context_->set_accessor_string("level_result", boost::bind(&playsingle_controller::describe_result, this)); plugins_context_->set_accessor_int("turn", boost::bind(&play_controller::turn, this)); } +std::string playsingle_controller::describe_result() const +{ + if(!is_regular_game_end()) { + return "NONE"; + } + else if(get_end_level_data_const().is_victory){ + return "VICTORY"; + } + else { + return "DEFEAT"; + } +} + playsingle_controller::~playsingle_controller() { ai::manager::remove_observer(this) ; @@ -117,7 +126,7 @@ void playsingle_controller::init_gui(){ } gui_->scroll_to_tile(gamestate_.board_.map().starting_position(1), game_display::WARP); - update_locker lock_display(gui_->video(),recorder.is_skipping()); + update_locker lock_display(gui_->video(), is_skipping_replay()); get_hotkey_command_executor()->set_button_state(*gui_); events::raise_draw_event(); gui_->draw(); @@ -190,7 +199,7 @@ void playsingle_controller::report_victory( report << '\n' << goldmsg; } -boost::optional playsingle_controller::play_scenario_init(end_level_data & /*eld*/, bool & past_prestart) { +void playsingle_controller::play_scenario_init() { // At the beginning of the scenario, save a snapshot as replay_start if(saved_game_.replay_start().empty()){ saved_game_.replay_start() = to_config(); @@ -198,11 +207,11 @@ boost::optional playsingle_controller::play_scenario_init(end_leve try { fire_preload(); - } catch (end_level_exception & e) { - return e.result; - } catch (restart_turn_exception &) { - assert(false && "caugh end_turn exception in a bad place... terminating."); - std::terminate(); + } catch (end_level_exception &e) { + if(e.is_quit) { + this->reset_end_level_data(); + } + return; } replaying_ = (recorder.at_end() == false); @@ -229,26 +238,25 @@ boost::optional playsingle_controller::play_scenario_init(end_leve try { fire_prestart(); - } catch (end_level_exception & e) { - return e.result; - } catch (restart_turn_exception &) { - assert(false && "caugh end_turn exception in a bad place... terminating."); - std::terminate(); + } catch (end_level_exception &e) { + if(e.is_quit) { + this->reset_end_level_data(); + } + return; } init_gui(); - past_prestart = true; - LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n"; + LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip") << "\n"; events::raise_draw_event(); try { - fire_start(true); - } catch (end_level_exception & e) { - return e.result; - } catch (restart_turn_exception &) { - assert(false && "caugh end_turn exception in a bad place... terminating."); - std::terminate(); + fire_start(); + } catch (end_level_exception &e) { + if(e.is_quit) { + this->reset_end_level_data(); + } + return; } sync.do_final_checkup(); gui_->recalculate_minimap(); @@ -256,22 +264,23 @@ boost::optional playsingle_controller::play_scenario_init(end_leve else { init_gui(); - past_prestart = true; events::raise_draw_event(); - try { - fire_start(false); - } catch (end_level_exception & e) { - return e.result; - } catch (restart_turn_exception &) { - assert(false && "caugh end_turn exception in a bad place... terminating."); - std::terminate(); - } + gamestate_.gamedata_.set_phase(game_data::PLAY); gui_->recalculate_minimap(); } - return boost::none; + if( saved_game_.classification().random_mode != "" && (network::nconnections() != 0)) { + // This won't cause errors later but we should notify the user about it in case he didn't knew it. + gui2::show_transient_message( + gui_->video(), + // TODO: find a better title + _("Game Error"), + _("This multiplayer game uses an alternative random mode, if you don't know what this message means, then most likeley someone is cheating or someone reloaded a corrupt game.") + ); + } + return; } -LEVEL_RESULT playsingle_controller::play_scenario_main_loop(end_level_data & end_level, bool & /*past_prestart*/) { +void playsingle_controller::play_scenario_main_loop() { LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n"; // Initialize countdown clock. @@ -282,16 +291,6 @@ LEVEL_RESULT playsingle_controller::play_scenario_main_loop(end_level_data & end } } - // if we loaded a save file in linger mode, skip to it. - if (linger_) { - //determine the bonus gold handling for this scenario - end_level.read(level_.child_or_empty("end_level_data")); - end_level.transient.carryover_report = false; - end_level.transient.disabled = true; - return SKIP_TO_LINGER; - //throw end_level_exception(SKIP_TO_LINGER); - } - // Avoid autosaving after loading, but still // allow the first turn to have an autosave. do_autosaves_ = !loading_game_; @@ -304,13 +303,10 @@ LEVEL_RESULT playsingle_controller::play_scenario_main_loop(end_level_data & end possible_end_play_signal signal = play_turn(); if (signal) { - switch (boost::apply_visitor( get_signal_type(), *signal )) { - case END_LEVEL: - return boost::apply_visitor( get_result(), *signal ); - case END_TURN: - assert(false && "end turn signal propagated to playsingle_controller::play_scenario_main_loop, terminating"); - std::terminate(); + if(signal->is_quit) { + reset_end_level_data(); } + return; } do_autosaves_ = true; @@ -349,23 +345,24 @@ LEVEL_RESULT playsingle_controller::play_scenario( set_victory_when_enemies_defeated(level_["victory_when_enemies_defeated"].to_bool(true)); set_remove_from_carryover_on_defeat(level_["remove_from_carryover_on_defeat"].to_bool(true)); - end_level_data &end_level = get_end_level_data(); - - bool past_prestart = false; LOG_NG << "entering try... " << (SDL_GetTicks() - ticks_) << "\n"; try { - boost::optional signal = play_scenario_init(end_level, past_prestart); - - if (!signal) { - signal = play_scenario_main_loop(end_level, past_prestart); + play_scenario_init(); + if (!is_regular_game_end() && !linger_) { + play_scenario_main_loop(); } - assert(signal); //play_scenario_main_loop always returns a LEVEL_RESULT { - LEVEL_RESULT end_level_result = *signal; + if (game_config::exit_at_end) { + exit(0); + } + if (!is_regular_game_end()) { + return QUIT; + } + const bool is_victory = get_end_level_data_const().is_victory; - if(!past_prestart) { + if(this->gamestate_.gamedata_.phase() <= game_data::PRESTART) { sdl::draw_solid_tinted_rectangle( 0, 0, gui_->video().getx(), gui_->video().gety(), 0, 0, 0, 1.0, gui_->video().getSurface() @@ -374,8 +371,10 @@ LEVEL_RESULT playsingle_controller::play_scenario( } ai_testing::log_game_end(); + + const end_level_data& end_level = get_end_level_data_const(); if (!end_level.transient.custom_endlevel_music.empty()) { - if (end_level_result == DEFEAT) { + if (!is_victory) { set_defeat_music_list(end_level.transient.custom_endlevel_music); } else { set_victory_music_list(end_level.transient.custom_endlevel_music); @@ -389,52 +388,44 @@ LEVEL_RESULT playsingle_controller::play_scenario( return VICTORY; // this is probably only a story scenario, i.e. has its endlevel in the prestart event } - const bool obs = is_observer(); - if (game_config::exit_at_end) { - exit(0); - } - if (end_level_result == DEFEAT || end_level_result == VICTORY) - { - saved_game_.classification().completion = (end_level_result == VICTORY) ? "victory" : "defeat"; - // 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"; - info["result"] = saved_game_.classification().completion; - network::send_data(cfg, 0); - } else { - gui2::show_transient_message(gui_->video(),_("Game Over"), - _("The game is over.")); - return OBSERVER_END; + if(linger_) { + LOG_NG << "resuming from loaded linger state...\n"; + //as carryover information is stored in the snapshot, we have to re-store it after loading a linger state + saved_game_.set_snapshot(config()); + if(!is_observer()) { + persist_.end_transaction(); } + return VICTORY; } - - if (end_level_result == QUIT) { - return QUIT; + saved_game_.classification().completion = is_victory ? "victory" : "defeat"; + if(is_observer()) { + gui2::show_transient_message(gui_->video(), _("Game Over"), _("The game is over.")); + return OBSERVER_END; } - else if (end_level_result == DEFEAT) + // 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. + { + config cfg; + config& info = cfg.add_child("info"); + info["type"] = "termination"; + info["condition"] = "game over"; + info["result"] = saved_game_.classification().completion; + network::send_data(cfg, 0); + } + if (!is_victory) { saved_game_.classification().completion = "defeat"; pump().fire("defeat"); - if (!obs) { - const std::string& defeat_music = select_defeat_music(); - if(defeat_music.empty() != true) - sound::play_music_once(defeat_music); - persist_.end_transaction(); - return DEFEAT; - } else { - return QUIT; - } + const std::string& defeat_music = select_defeat_music(); + if(defeat_music.empty() != true) + sound::play_music_once(defeat_music); + persist_.end_transaction(); + return DEFEAT; } - else if (end_level_result == VICTORY) + else { - saved_game_.classification().completion = - - !end_level.transient.linger_mode ? "running" : "victory"; + saved_game_.classification().completion = !end_level.transient.linger_mode ? "running" : "victory"; pump().fire("victory"); // @@ -446,7 +437,7 @@ LEVEL_RESULT playsingle_controller::play_scenario( // a victory, so let them use [music] tags // instead should they want special music. // - if (!obs && end_level.transient.linger_mode) { + if (end_level.transient.linger_mode) { const std::string& victory_music = select_victory_music(); if(victory_music.empty() != true) sound::play_music_once(victory_music); @@ -456,23 +447,10 @@ LEVEL_RESULT playsingle_controller::play_scenario( gamestate_.board_.heal_all_survivors(); saved_game_.remove_snapshot(); - if(!is_observer()) { - persist_.end_transaction(); - } - + persist_.end_transaction(); return VICTORY; } - else if (end_level_result == SKIP_TO_LINGER) - { - LOG_NG << "resuming from loaded linger state...\n"; - //as carryover information is stored in the snapshot, we have to re-store it after loading a linger state - saved_game_.set_snapshot(config()); - if(!is_observer()) { - persist_.end_transaction(); - } - return VICTORY; - } - } //end if + } //end block } catch(const game::load_game_exception &) { // Loading a new game is effectively a quit. // @@ -548,6 +526,7 @@ possible_end_play_signal playsingle_controller::play_turn() } //If the loop exits due to the last team having been processed, //player_number_ will be 1 too high + //TODO: Why else could the loop exit? if(player_number_ > static_cast(gamestate_.board_.teams().size())) player_number_ = gamestate_.board_.teams().size(); @@ -560,7 +539,7 @@ possible_end_play_signal playsingle_controller::play_turn() possible_end_play_signal playsingle_controller::play_idle_loop() { - while(!end_turn_) { + while(!end_turn_ && !player_type_changed_) { HANDLE_END_PLAY_SIGNAL( play_slice() ); gui_->draw(); SDL_Delay(10); @@ -577,8 +556,6 @@ possible_end_play_signal playsingle_controller::play_side() maybe_do_init_side(); } catch (end_level_exception & e) { return possible_end_play_signal(e.to_struct()); - } catch (restart_turn_exception &) { - // ignore it. Since we don't have started the loop below yet, we dont need to restart it. } //flag used when we fallback from ai and give temporarily control to human bool temporary_human = false; @@ -603,23 +580,18 @@ possible_end_play_signal playsingle_controller::play_side() if (!end_turn_) { signal = play_human_turn(); } - - if (signal) { - switch (boost::apply_visitor(get_signal_type(), *signal)) { - case END_LEVEL: - return signal; - case END_TURN: { - player_type_changed_ = true; - // If new controller is not human, - // reset gui to prev human one - if (!gamestate_.board_.teams()[player_number_-1].is_local_human()) { - int s = find_human_team_before_current_player(); - if (s <= 0) { - s = gui_->playing_side(); - } - update_gui_to_player(s-1); - } + if (signal) { + return signal; + } + if (player_type_changed_) { + // If new controller is not human, + // reset gui to prev human one + if (!gamestate_.board_.teams()[player_number_-1].is_local_human()) { + int s = find_human_team_before_current_player(); + if (s <= 0) { + s = gui_->playing_side(); } + update_gui_to_player(s-1); } } } @@ -635,10 +607,8 @@ possible_end_play_signal playsingle_controller::play_side() // Give control to a human for this turn. player_type_changed_ = true; temporary_human = true; - } catch (end_level_exception & e) { + } catch (end_level_exception &e) { return possible_end_play_signal(e.to_struct()); - } catch (restart_turn_exception & e) { - player_type_changed_ = true; } if(!player_type_changed_) { @@ -657,30 +627,27 @@ possible_end_play_signal playsingle_controller::play_side() if (!end_turn_) { signal = play_idle_loop(); } - + if (signal) { - switch (boost::apply_visitor(get_signal_type(), *signal)) { - case END_LEVEL: - return signal; - case END_TURN: { - LOG_NG << "Escaped from idle state with exception!" << std::endl; - player_type_changed_ = true; - // If new controller is not human, - // reset gui to prev human one - if (!gamestate_.board_.teams()[player_number_-1].is_local_human()) { - int s = find_human_team_before_current_player(); - if (s <= 0) { - s = gui_->playing_side(); - } - update_gui_to_player(s-1); - } - else { - //This side was previously not human controlled. - update_gui_to_player(player_number_ - 1); - } + return signal; + } + if (player_type_changed_) { + // If new controller is not human, + // reset gui to prev human one + if (!gamestate_.board_.teams()[player_number_-1].is_local_human()) { + int s = find_human_team_before_current_player(); + if (s <= 0) { + s = gui_->playing_side(); } + update_gui_to_player(s-1); } + else { + //This side was previously not human controlled. + update_gui_to_player(player_number_ - 1); + } + } + } else { assert(current_team().is_empty()); // Do nothing. @@ -700,21 +667,22 @@ void playsingle_controller::before_human_turn() if(end_turn_) { return; } + //TODO: why do we need the next line? ai::manager::raise_turn_started(); - if(do_autosaves_ && level_result_ == NONE) { + if(do_autosaves_ && !is_regular_game_end()) { update_savegame_snapshot(); savegame::autosave_savegame save(saved_game_, *gui_, preferences::save_compression_format()); save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES); } - if(preferences::turn_bell() && level_result_ == NONE) { + if(preferences::turn_bell() && !is_regular_game_end()) { sound::play_bell(game_config::sounds::turn_bell); } } void playsingle_controller::show_turn_dialog(){ - if(preferences::turn_dialog() && (level_result_ == NONE) ) { + if(preferences::turn_dialog() && !is_regular_game_end() ) { blindfold b(*gui_, true); //apply a blindfold for the duration of this dialog gui_->redraw_everything(); gui_->recalculate_minimap(); @@ -738,7 +706,7 @@ possible_end_play_signal playsingle_controller::play_human_turn() { } end_turn_enable(true); - while(!end_turn_) { + while(!end_turn_ && !player_type_changed_) { HANDLE_END_PLAY_SIGNAL( play_slice() ); gui_->draw(); } @@ -898,12 +866,11 @@ possible_end_play_signal playsingle_controller::check_time_over(){ } HANDLE_END_PLAY_SIGNAL( check_victory() ); - - get_end_level_data().proceed_to_next_level = false; - - end_level_struct els = {DEFEAT}; - return possible_end_play_signal (els); - //throw end_level_exception(DEFEAT); + end_level_data e; + e.proceed_to_next_level = false; + e.is_victory = false; + set_end_level_data(e); + return possible_end_play_signal (); } return boost::none; } @@ -923,7 +890,7 @@ void playsingle_controller::force_end_turn(){ void playsingle_controller::check_end_level() { - if (level_result_ == NONE || linger_) + if (!is_regular_game_end() || linger_) { const team &t = gamestate_.board_.teams()[gui_->viewing_team()]; if (!is_browsing() && t.objectives_changed()) { @@ -932,7 +899,7 @@ void playsingle_controller::check_end_level() } return; } - throw end_level_exception(level_result_); + throw end_level_exception(); } @@ -943,9 +910,9 @@ bool playsingle_controller::is_host() const void playsingle_controller::maybe_linger() { - //Make sure [end_level_data] gets writen into the snapshot even when skipping linger mode. - linger_ = true; - if (get_end_level_data_const().transient.linger_mode) { + // mouse_handler expects at least one team for linger mode to work. + assert(is_regular_game_end()); + if (get_end_level_data_const().transient.linger_mode && !gamestate_.board_.teams().empty()) { linger(); } } diff --git a/src/playsingle_controller.hpp b/src/playsingle_controller.hpp index 281d514593dd..9ba135195208 100644 --- a/src/playsingle_controller.hpp +++ b/src/playsingle_controller.hpp @@ -49,13 +49,11 @@ class playsingle_controller : public play_controller LEVEL_RESULT play_scenario(const config::const_child_itors &story, bool skip_replay); - boost::optional play_scenario_init(end_level_data & eld, bool & past_prestart ); - LEVEL_RESULT play_scenario_main_loop(end_level_data & eld, bool & past_prestart ); + void play_scenario_init(); + void play_scenario_main_loop(); virtual void handle_generic_event(const std::string& name); - virtual void force_end_level(LEVEL_RESULT res) - { level_result_ = res; } virtual void check_end_level(); void report_victory(std::ostringstream &report, team& t, int finishing_bonus_per_turn, int turns_left, int finishing_bonus); @@ -68,11 +66,14 @@ class playsingle_controller : public play_controller class hotkey_handler; virtual bool is_end_turn() const { return end_turn_; } - + std::string describe_result() const; + + bool get_player_type_changed() const { return player_type_changed_; } + void set_player_type_changed() { player_type_changed_ = true; } protected: possible_end_play_signal play_turn(); virtual possible_end_play_signal play_side(); - virtual void before_human_turn(); + void before_human_turn(); void show_turn_dialog(); void execute_gotos(); virtual possible_end_play_signal play_human_turn(); @@ -98,7 +99,6 @@ class playsingle_controller : public play_controller bool replaying_; bool skip_next_turn_; bool do_autosaves_; - LEVEL_RESULT level_result_; void linger(); }; diff --git a/src/playturn.cpp b/src/playturn.cpp index 90ede4c8de63..ee5d631f0753 100644 --- a/src/playturn.cpp +++ b/src/playturn.cpp @@ -75,7 +75,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::sync_network() //could get data back pertaining to the next turn. config cfg; while( (retv == turn_info::PROCESS_CONTINUE) && network_reader_.read(cfg)) { - retv = process_network_data(cfg,false); + retv = process_network_data(cfg); cfg.clear(); } send_data(); @@ -92,20 +92,15 @@ void turn_info::send_data() } } -turn_info::PROCESS_DATA_RESULT turn_info::handle_turn( - const config& t, - const bool skip_replay) +turn_info::PROCESS_DATA_RESULT turn_info::handle_turn(const config& t) { //t can contain a [command] or a [upload_log] assert(t.all_children_count() == 1); /** @todo FIXME: Check what commands we execute when it's our turn! */ - bool was_skipping = recorder.is_skipping(); - recorder.set_skip(skip_replay); //note, that this function might call itself recursively: do_replay -> ... -> persist_var -> ... -> handle_generic_event -> sync_network -> handle_turn recorder.add_config(t, replay::MARK_AS_SENT); PROCESS_DATA_RESULT retv = replay_to_process_data_result(do_replay()); - recorder.set_skip(was_skipping); return retv; } @@ -116,12 +111,12 @@ void turn_info::do_save() } } -turn_info::PROCESS_DATA_RESULT turn_info::process_network_data_from_reader(bool skip_replay) +turn_info::PROCESS_DATA_RESULT turn_info::process_network_data_from_reader() { config cfg; while(this->network_reader_.read(cfg)) { - PROCESS_DATA_RESULT res = process_network_data(cfg, skip_replay); + PROCESS_DATA_RESULT res = process_network_data(cfg); if(res != PROCESS_CONTINUE) { return res; @@ -130,7 +125,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data_from_reader(bool return PROCESS_CONTINUE; } -turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg, bool skip_replay) +turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg) { // we cannot be connected to multiple peers anymore because // the simple wesnothserver implementation in wesnoth was removed years ago. @@ -167,7 +162,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg } else if (const config &turn = cfg.child("turn")) { - return handle_turn(turn, skip_replay); + return handle_turn(turn); } else if (cfg.has_child("whiteboard")) { @@ -334,7 +329,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg //The user pressed "end game". Don't throw a network error here or he will get //thrown back to the title screen. do_save(); - throw end_level_exception(QUIT); + throw_quit_game_exception(); default: if (action > 3) { diff --git a/src/playturn.hpp b/src/playturn.hpp index d54a333f7a47..0480e91a4e26 100644 --- a/src/playturn.hpp +++ b/src/playturn.hpp @@ -50,10 +50,10 @@ class turn_info void send_data(); //function which will process incoming network data received with playturn_network_adapter, and act on it. - PROCESS_DATA_RESULT process_network_data(const config& cfg, bool skip_replay); + PROCESS_DATA_RESULT process_network_data(const config& cfg); //reads as much data from network_reader_ as possible and processed it. - PROCESS_DATA_RESULT process_network_data_from_reader(bool skip_replay); + PROCESS_DATA_RESULT process_network_data_from_reader(); events::generic_event& host_transfer() { return host_transfer_; } @@ -64,9 +64,7 @@ class turn_info static void change_controller(int side, const std::string& controller); static void change_side_controller(int side, const std::string& player); static PROCESS_DATA_RESULT replay_to_process_data_result(REPLAY_RETURN replayreturn); - PROCESS_DATA_RESULT handle_turn( - const config& t, - const bool skip_replay); + PROCESS_DATA_RESULT handle_turn(const config& t); void do_save(); diff --git a/src/replay.cpp b/src/replay.cpp index e81a4a0ee563..1efcd3ad38d4 100644 --- a/src/replay.cpp +++ b/src/replay.cpp @@ -186,14 +186,12 @@ chat_msg::~chat_msg() replay::replay() : cfg_(), pos_(0), - skip_(false), message_locations() {} replay::replay(const config& cfg) : cfg_(cfg), pos_(0), - skip_(false), message_locations() {} @@ -215,16 +213,6 @@ void replay::process_error(const std::string& msg) resources::controller->process_oos(msg); // might throw end_level_exception(QUIT) } -void replay::set_skip(bool skip) -{ - skip_ = skip; -} - -bool replay::is_skipping() const -{ - return skip_; -} - void replay::add_unit_checksum(const map_location& loc,config& cfg) { if(! game_config::mp_debug) { @@ -648,7 +636,6 @@ void replay::clear() message_log.clear(); cfg_ = config(); pos_ = 0; - skip_ = false; } bool replay::empty() @@ -721,11 +708,11 @@ REPLAY_RETURN do_replay(bool one_move) { log_scope("do replay"); - if (!get_replay_source().is_skipping()){ + if (!resources::controller->is_skipping_replay()) { resources::screen->recalculate_minimap(); } - update_locker lock_update(resources::screen->video(),get_replay_source().is_skipping()); + update_locker lock_update(resources::screen->video(), resources::controller->is_skipping_replay()); return do_replay_handle(one_move); } @@ -769,7 +756,7 @@ REPLAY_RETURN do_replay_handle(bool one_move) //if (!preferences::parse_should_show_lobby_join(speaker_name, message)) return; bool is_whisper = (speaker_name.find("whisper: ") == 0); get_replay_source().add_chat_message_location(); - if (!get_replay_source().is_skipping() || is_whisper) { + if (!resources::controller->is_skipping_replay() || is_whisper) { int side = child["side"]; resources::screen->get_chat_manager().add_chat_message(get_time(child), speaker_name, side, message, (team_name.empty() ? events::chat_handler::MESSAGE_PUBLIC @@ -905,7 +892,7 @@ REPLAY_RETURN do_replay_handle(bool one_move) /* we need to use the undo stack during replays in order to make delayed shroud updated work. */ - synced_context::run_in_synced_context(commandname, data, true, !get_replay_source().is_skipping(), false,show_oos_error_error_function); + synced_context::run_in_synced_context(commandname, data, true, !resources::controller->is_skipping_replay(), false,show_oos_error_error_function); if (one_move) { return REPLAY_FOUND_END_MOVE; } diff --git a/src/replay.hpp b/src/replay.hpp index 1d3c471fd4f7..557a0d1eefa3 100644 --- a/src/replay.hpp +++ b/src/replay.hpp @@ -55,9 +55,6 @@ class replay void append(const config& cfg); - void set_skip(bool skip); - bool is_skipping() const; - void add_start(); void add_countdown_update(int value,int team); @@ -152,8 +149,6 @@ class replay config cfg_; int pos_; - bool skip_; - std::vector message_locations; }; diff --git a/src/replay_controller.cpp b/src/replay_controller.cpp index fe7768bf4253..cc571b28b25c 100644 --- a/src/replay_controller.cpp +++ b/src/replay_controller.cpp @@ -67,8 +67,9 @@ LEVEL_RESULT play_replay_level(const config& game_config, const tdata_cache & td try { rc.reset(new replay_controller(state_of_game.get_replay_starting_pos(), state_of_game, ticks, game_config, tdata, video)); - } catch (end_level_exception & e){ - return e.result; + } catch (end_level_exception&){ + //TODO: When can this happen? + return QUIT; } DBG_NG << "created objects... " << (SDL_GetTicks() - rc->get_ticks()) << std::endl; @@ -76,15 +77,7 @@ LEVEL_RESULT play_replay_level(const config& game_config, const tdata_cache & td possible_end_play_signal signal = play_replay_level_main_loop(*rc, is_unit_test); if (signal) { - switch( boost::apply_visitor( get_signal_type(), *signal ) ) { - case END_LEVEL: - DBG_NG << "play_replay_level: end_level_exception" << std::endl; - break; - case END_TURN: - DBG_NG << "Unchecked end_turn_exception signal propogated to replay controller play_replay_level! Terminating." << std::endl; - assert(false && "unchecked end turn exception in replay controller"); - throw 42; - } + DBG_NG << "play_replay_level: end_level_exception" << std::endl; } return VICTORY; @@ -324,7 +317,6 @@ void replay_controller::reset_replay() skip_replay_ = false; gamestate_.tod_manager_= tod_manager_start_; recorder.start_replay(); - recorder.set_skip(false); saved_game_ = saved_game_start_; gamestate_.board_ = gameboard_start_; gui_->change_display_context(&gamestate_.board_); //this doesn't change the pointer value, but it triggers the gui to update the internal terrain builder object, @@ -380,11 +372,9 @@ void replay_controller::reset_replay() fire_prestart(); init_gui(); - fire_start(true); + fire_start(); sync.do_final_checkup(); } - // Since we did not fire the start event, it_is_a_new_turn_ has the wrong value. - it_is_a_new_turn_ = true; update_gui(); reset_replay_ui(); @@ -400,7 +390,7 @@ possible_end_play_signal replay_controller::replay_next_turn(){ PROPOGATE_END_PLAY_SIGNAL( play_turn() ); - if (!skip_replay_ || !is_playing_){ + if (!is_skipping_replay() || !is_playing_){ gui_->scroll_to_leader(player_number_,game_display::ONSCREEN,false); } @@ -417,7 +407,7 @@ possible_end_play_signal replay_controller::replay_next_move_or_side(bool one_mo HANDLE_END_PLAY_SIGNAL( play_move_or_side(one_move) ); } - if ( (!skip_replay_ || !is_playing_) && (last_replay_action == REPLAY_FOUND_END_TURN) ){ + if ( (!is_skipping_replay() || !is_playing_) && (last_replay_action == REPLAY_FOUND_END_TURN) ){ gui_->scroll_to_leader(player_number_,game_display::ONSCREEN,false); } @@ -445,7 +435,7 @@ void replay_controller::process_oos(const std::string& msg) const message << "\n\n" << _("Error details:") << "\n\n" << msg; if (non_interactive()) { - throw game::game_error(message.str()); //throw end_level_exception(DEFEAT); + throw game::game_error(message.str()); } else { update_savegame_snapshot(); savegame::oos_savegame save(saved_game_, *gui_); @@ -476,7 +466,6 @@ void replay_controller::replay_show_team1(){ void replay_controller::replay_skip_animation(){ skip_replay_ = !skip_replay_; - recorder.set_skip(skip_replay_); } //move all sides till stop/end @@ -493,15 +482,9 @@ possible_end_play_signal replay_controller::play_replay(){ possible_end_play_signal signal = play_replay_main_loop(); if(signal) { - switch ( boost::apply_visitor(get_signal_type(), *signal)) { - case END_TURN: - return signal; - case END_LEVEL: - if ( boost::apply_visitor(get_result(), *signal) == QUIT) { - return signal; - } + if(signal->is_quit) { + return signal; } - } if (!is_playing_) { @@ -577,8 +560,8 @@ possible_end_play_signal replay_controller::play_move_or_side(bool one_move) { return boost::none; } } catch(end_level_exception& e){ - //VICTORY/DEFEAT end_level_exception shall not return to title screen - if (e.result != VICTORY && e.result != DEFEAT) { + //dont return to title screen if the game ended the normal way + if (!is_regular_game_end()) { return possible_end_play_signal(e.to_struct()); } } @@ -644,7 +627,7 @@ void replay_controller::handle_generic_event(const std::string& name){ gui::button* skip_animation_button = gui_->find_action_button("skip-animation"); if(skip_animation_button) { - skip_animation_button->set_check(skip_replay_); + skip_animation_button->set_check(is_skipping_replay()); } } else { rebuild_replay_theme(); diff --git a/src/replay_controller.hpp b/src/replay_controller.hpp index 541921e52dac..a0d3b50a0b03 100644 --- a/src/replay_controller.hpp +++ b/src/replay_controller.hpp @@ -46,7 +46,6 @@ class replay_controller : public play_controller void replay_skip_animation(); virtual void force_end_turn() {} - virtual void force_end_level(LEVEL_RESULT) {} virtual void check_end_level() {} virtual void on_not_observer() {} diff --git a/src/savegame.cpp b/src/savegame.cpp index 77331b3ba24e..ae81a35bc5ee 100644 --- a/src/savegame.cpp +++ b/src/savegame.cpp @@ -405,7 +405,7 @@ bool savegame::save_game_interactive(CVideo& video, const std::string& message, const int res = show_save_dialog(video, message, dialog_type); if (res == 2) { - throw end_level_exception(QUIT); //Quit game + throw_quit_game_exception(); //Quit game } if (res == gui2::twindow::OK && check_overwrite(video)) { diff --git a/src/scripting/game_lua_kernel.cpp b/src/scripting/game_lua_kernel.cpp index 8c79a0893ffd..4f55fb6c580f 100644 --- a/src/scripting/game_lua_kernel.cpp +++ b/src/scripting/game_lua_kernel.cpp @@ -1401,7 +1401,7 @@ static int intf_debug(lua_State* L) int game_lua_kernel::intf_check_end_level_disabled(lua_State * L) { - lua_pushboolean(L, play_controller_.get_end_level_data().transient.disabled); + lua_pushboolean(L, play_controller_.is_regular_game_end()); return 1; } @@ -1441,13 +1441,12 @@ int game_lua_kernel::intf_end_level(lua_State *L) { vconfig cfg(luaW_checkvconfig(L, 1)); - end_level_data &data = play_controller_.get_end_level_data(); - - if (data.transient.disabled) { + + if (play_controller_.is_regular_game_end()) { return 0; } - data.transient.disabled = true; - + end_level_data data; + // TODO: is this still needed? // Remove 0-hp units from the unit map to avoid the following problem: // In case a die event triggers an endlevel the dead unit is still as a @@ -1548,12 +1547,8 @@ int game_lua_kernel::intf_end_level(lua_State *L) data.transient.linger_mode = cfg["linger_mode"].to_bool(true) && !teams().empty(); data.transient.reveal_map = cfg["reveal_map"].to_bool(true); - if(!local_human_victory) { - play_controller_.force_end_level(DEFEAT); - } else { - play_controller_.force_end_level(VICTORY); - } - + data.is_victory = local_human_victory; + play_controller_.set_end_level_data(data); return 0; } diff --git a/src/whiteboard/move.cpp b/src/whiteboard/move.cpp index 4a658593d6a7..cf5ad6e42171 100644 --- a/src/whiteboard/move.cpp +++ b/src/whiteboard/move.cpp @@ -226,6 +226,7 @@ void move::execute(bool& success, bool& complete) events::mouse_handler& mouse_handler = resources::controller->get_mouse_handler_base(); num_steps = mouse_handler.move_unit_along_route(steps, interrupted); } catch (restart_turn_exception&) { + //This exception is thown by the ai code, i dont know whther the whiteboard can interact with the ai so i leave this catch here for now set_arrow_brightness(ARROW_BRIGHTNESS_STANDARD); throw; // we rely on the caller to delete this action } diff --git a/src/whiteboard/side_actions.cpp b/src/whiteboard/side_actions.cpp index 974dbfbc0cdb..7a4c87fa50af 100644 --- a/src/whiteboard/side_actions.cpp +++ b/src/whiteboard/side_actions.cpp @@ -337,6 +337,7 @@ bool side_actions::execute(side_actions::iterator position) try { action->execute(action_successful, action_complete); } catch (restart_turn_exception&) { + //This exception is thown by the ai code, i dont know whther the whiteboard can interact with the ai so i leave this catch here for now synced_erase(position); LOG_WB << "End turn exception caught during execution, deleting action. " << *this << "\n"; //validate actions at next map rebuild