From 30e670f5a8babd17fa5647d1ef12176a04be44de Mon Sep 17 00:00:00 2001 From: gfgtdf Date: Sat, 28 Feb 2015 23:38:48 +0100 Subject: [PATCH] refactor end_level_data, end_level_exception and LEVEL_RESULT I removed the NONE value of LEVEL_RESULT because 'LEVEL_RESULT==NONE' was basicly the same information as '!end_level_data.transient.disabled' I removed end_level_data.transient.disabled and instead store the end level_data as an optional to remember whether we already have an end_level_data I removed the SKIP_TO_LINGER value of LEVEL_RESULT because it was not possible that play_scenario returned SKIP_TO_LINGER (so i removed that code in playcampaign.cpp that handled this case) I replaced playsingle_controller::level_result_ and end_level_exception/struct::result which both held more or less the same data with end_level_data::is_vitory, in case of QUIT the optional is empty (none_t) I also reordered the if cases in playsingle_controller::play_scenario to simplify. --- src/game_end_exceptions.cpp | 1 - src/game_end_exceptions.hpp | 37 ++-- src/game_initialization/playcampaign.cpp | 33 ++-- src/game_launcher.cpp | 6 +- src/hotkey/command_executor.cpp | 4 +- src/menu_events.cpp | 14 +- src/play_controller.cpp | 27 +-- src/play_controller.hpp | 18 +- src/playmp_controller.cpp | 6 +- src/playsingle_controller.cpp | 208 +++++++++++------------ src/playsingle_controller.hpp | 9 +- src/playturn.cpp | 2 +- src/replay_controller.cpp | 14 +- src/replay_controller.hpp | 1 - src/savegame.cpp | 2 +- src/scripting/game_lua_kernel.cpp | 19 +-- 16 files changed, 193 insertions(+), 208 deletions(-) diff --git a/src/game_end_exceptions.cpp b/src/game_end_exceptions.cpp index fb056db41d3e..b77a305c6355 100644 --- a/src/game_end_exceptions.cpp +++ b/src/game_end_exceptions.cpp @@ -23,7 +23,6 @@ transient_end_level::transient_end_level() , linger_mode(true) , custom_endlevel_music() , reveal_map(true) - , disabled(false) {} end_level_data::end_level_data() diff --git a/src/game_end_exceptions.hpp b/src/game_end_exceptions.hpp index 3abb6c7d0541..1b35588bf755 100644 --- a/src/game_end_exceptions.hpp +++ b/src/game_end_exceptions.hpp @@ -33,12 +33,10 @@ #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) @@ -96,7 +94,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 +106,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; } @@ -188,20 +186,6 @@ class get_signal_type : public boost::static_visitor { } }; -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 +197,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 +211,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 +223,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_initialization/playcampaign.cpp b/src/game_initialization/playcampaign.cpp index 13ceba78ece5..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,25 +189,20 @@ static LEVEL_RESULT playsingle_scenario(const config& game_config, LEVEL_RESULT res = playcontroller.play_scenario(story, skip_replay); - end_level = playcontroller.get_end_level_data_const(); - if (res == QUIT) { return QUIT; } - //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); - } + + end_level = playcontroller.get_end_level_data_const(); + + 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; - } + } catch(end_level_exception&) { + return QUIT; } } state_of_game.set_snapshot(playcontroller.to_config()); @@ -226,8 +222,6 @@ 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; @@ -236,7 +230,10 @@ static LEVEL_RESULT playmp_scenario(const config& game_config, { return QUIT; } - if(res != OBSERVER_END && res != SKIP_TO_LINGER) + + 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) @@ -246,10 +243,8 @@ static LEVEL_RESULT playmp_scenario(const config& game_config, { try { playcontroller.maybe_linger(); - } catch(end_level_exception& e) { - if (e.result == QUIT) { - return QUIT; - } + } catch(end_level_exception&) { + return QUIT; } } playcontroller.update_savegame_snapshot(); 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/menu_events.cpp b/src/menu_events.cpp index 0f7a221f5295..a8cb851f32fc 100644 --- a/src/menu_events.cpp +++ b/src/menu_events.cpp @@ -2950,7 +2950,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 +2963,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 +3013,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/play_controller.cpp b/src/play_controller.cpp index 1a12897ff809..3c202f22779c 100644 --- a/src/play_controller.cpp +++ b/src/play_controller.cpp @@ -163,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 @@ -185,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(); @@ -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(){ @@ -452,8 +457,8 @@ config play_controller::to_config() const 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 @@ -821,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); @@ -885,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 4e9f9d2fdaea..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_; @@ -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 cd8df332bc6e..f44e075cb16f 100644 --- a/src/playmp_controller.cpp +++ b/src/playmp_controller.cpp @@ -338,7 +338,7 @@ void playmp_controller::wait_for_upload() } else { - throw end_level_exception(QUIT); + throw_quit_game_exception(); } } catch(const end_level_exception&) { @@ -483,8 +483,8 @@ 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. + 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/playsingle_controller.cpp b/src/playsingle_controller.cpp index 34ea1df4c98a..55e25a7b61ac 100644 --- a/src/playsingle_controller.cpp +++ b/src/playsingle_controller.cpp @@ -56,6 +56,7 @@ #include "hotkey/hotkey_item.hpp" #include +#include static lg::log_domain log_aitesting("aitesting"); #define LOG_AIT LOG_STREAM(info, log_aitesting) @@ -83,25 +84,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) ; @@ -190,7 +200,7 @@ void playsingle_controller::report_victory( report << '\n' << goldmsg; } -boost::optional playsingle_controller::play_scenario_init(end_level_data & /*eld*/) { +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,8 +208,11 @@ boost::optional playsingle_controller::play_scenario_init(end_leve try { fire_preload(); - } catch (end_level_exception & e) { - return e.result; + } catch (end_level_exception &e) { + if(e.is_quit) { + this->reset_end_level_data(); + } + return; } catch (restart_turn_exception &) { assert(false && "caugh end_turn exception in a bad place... terminating."); std::terminate(); @@ -229,9 +242,12 @@ boost::optional playsingle_controller::play_scenario_init(end_leve try { fire_prestart(); - } catch (end_level_exception & e) { - return e.result; - } catch (restart_turn_exception &) { + } catch (end_level_exception &e) { + if(e.is_quit) { + this->reset_end_level_data(); + } + return; + } catch (restart_turn_exception&) { assert(false && "caugh end_turn exception in a bad place... terminating."); std::terminate(); } @@ -243,11 +259,13 @@ boost::optional playsingle_controller::play_scenario_init(end_leve events::raise_draw_event(); try { fire_start(); - } catch (end_level_exception & e) { - return e.result; + } catch (end_level_exception &e) { + if(e.is_quit) { + this->reset_end_level_data(); + } + return; } catch (restart_turn_exception &) { - assert(false && "caugh end_turn exception in a bad place... terminating."); - std::terminate(); + //someone decided to droid a side during start event. Ignore it. } sync.do_final_checkup(); gui_->recalculate_minimap(); @@ -268,10 +286,10 @@ boost::optional playsingle_controller::play_scenario_init(end_leve _("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 boost::none; + return; } -LEVEL_RESULT playsingle_controller::play_scenario_main_loop(end_level_data & end_level) { +void playsingle_controller::play_scenario_main_loop() { LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n"; // Initialize countdown clock. @@ -282,16 +300,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_; @@ -306,7 +314,10 @@ LEVEL_RESULT playsingle_controller::play_scenario_main_loop(end_level_data & end if (signal) { switch (boost::apply_visitor( get_signal_type(), *signal )) { case END_LEVEL: - return boost::apply_visitor( get_result(), *signal ); + if(boost::get(*signal).is_quit) { + reset_end_level_data(); + } + return; case END_TURN: assert(false && "end turn signal propagated to playsingle_controller::play_scenario_main_loop, terminating"); std::terminate(); @@ -349,19 +360,22 @@ 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(); LOG_NG << "entering try... " << (SDL_GetTicks() - ticks_) << "\n"; try { - boost::optional signal = play_scenario_init(end_level); - - if (!signal) { - signal = play_scenario_main_loop(end_level); + 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(this->gamestate_.gamedata_.phase() <= game_data::PRESTART) { sdl::draw_solid_tinted_rectangle( @@ -372,8 +386,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); @@ -387,52 +403,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; + } + // 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); } - else if (end_level_result == DEFEAT) + 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"); // @@ -444,7 +452,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); @@ -454,23 +462,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. // @@ -546,6 +541,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(); @@ -633,9 +629,9 @@ 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) { + } catch (restart_turn_exception&) { player_type_changed_ = true; } if(!player_type_changed_) @@ -700,19 +696,19 @@ void playsingle_controller::before_human_turn() } 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(); @@ -896,12 +892,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; } @@ -921,7 +916,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()) { @@ -930,7 +925,7 @@ void playsingle_controller::check_end_level() } return; } - throw end_level_exception(level_result_); + throw end_level_exception(); } @@ -941,9 +936,8 @@ 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. + 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 f6b05c8d7a8b..a2f6ac7d434c 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); - LEVEL_RESULT play_scenario_main_loop(end_level_data & eld); + 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,7 +66,7 @@ class playsingle_controller : public play_controller class hotkey_handler; virtual bool is_end_turn() const { return end_turn_; } - + std::string describe_result() const; protected: possible_end_play_signal play_turn(); virtual possible_end_play_signal play_side(); @@ -98,7 +96,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 4f153598e5c5..ee5d631f0753 100644 --- a/src/playturn.cpp +++ b/src/playturn.cpp @@ -329,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/replay_controller.cpp b/src/replay_controller.cpp index 6dc24237c5fb..b0e967b1477f 100644 --- a/src/replay_controller.cpp +++ b/src/replay_controller.cpp @@ -43,6 +43,7 @@ #include #include #include +#include static lg::log_domain log_engine("engine"); #define DBG_NG LOG_STREAM(debug, log_engine) @@ -67,8 +68,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; @@ -442,7 +444,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_); @@ -493,7 +495,7 @@ possible_end_play_signal replay_controller::play_replay(){ case END_TURN: return signal; case END_LEVEL: - if ( boost::apply_visitor(get_result(), *signal) == QUIT) { + if(boost::get(*signal).is_quit) { return signal; } } @@ -573,8 +575,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()); } } 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; }