Logo Search packages:      
Sourcecode: wesnoth-1.8 version File versions  Download package

playsingle_controller.cpp

Go to the documentation of this file.
/* $Id: playsingle_controller.cpp 41661 2010-03-21 19:10:07Z jhinrichs $ */

/*
   Copyright (C) 2006 - 2010 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
   wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
   Part of the Battle for Wesnoth Project http://www.wesnoth.org/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2
   or at your option any later version.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

/**
 *  @file playsingle_controller.cpp
 *  Logic for single-player game.
 */

#include "playsingle_controller.hpp"

#include "actions.hpp"
#include "ai/manager.hpp"
#include "ai/game_info.hpp"
#include "ai/testing.hpp"
#include "foreach.hpp"
#include "game_end_exceptions.hpp"
#include "game_events.hpp"
#include "game_preferences.hpp"
#include "gettext.hpp"
#include "gui/dialogs/transient_message.hpp"
#include "log.hpp"
#include "map_label.hpp"
#include "marked-up_text.hpp"
#include "playturn.hpp"
#include "resources.hpp"
#include "savegame.hpp"
#include "sound.hpp"
#include "upload_log.hpp"
#include "formula_string_utils.hpp"
#include "events.hpp"
#include "save_blocker.hpp"
#include "soundsource.hpp"
#include "storyscreen/interface.hpp"

static lg::log_domain log_engine("engine");
#define ERR_NG LOG_STREAM(err, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)

playsingle_controller::playsingle_controller(const config& level,
            game_state& state_of_game, const int ticks, const int num_turns,
            const config& game_config, CVideo& video, bool skip_replay) :
      play_controller(level, state_of_game, ticks, num_turns, game_config, video, skip_replay),
      cursor_setter(cursor::NORMAL),
      data_backlog_(),
      textbox_info_(),
      replay_sender_(recorder),
      end_turn_(false),
      player_type_changed_(false),
      replaying_(false),
      turn_over_(false),
      skip_next_turn_(false),
      level_result_(NONE)
{
      // 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";
            browse_ = linger_ = true;
      }

      ai::game_info ai_info(*gui_,map_,units_,teams_, tod_manager_, gamestate_);
      ai::manager::set_ai_info(ai_info);
      ai::manager::add_observer(this) ;
}

playsingle_controller::~playsingle_controller()
{
      ai::manager::remove_observer(this) ;
      ai::manager::clear_ais() ;
}

void playsingle_controller::init_gui(){
      LOG_NG << "Initializing GUI... " << (SDL_GetTicks() - ticks_) << "\n";
      play_controller::init_gui();

      if(first_human_team_ != -1) {
            gui_->scroll_to_tile(map_.starting_position(first_human_team_ + 1), game_display::WARP);
      }
      gui_->scroll_to_tile(map_.starting_position(1), game_display::WARP);

      update_locker lock_display(gui_->video(),recorder.is_skipping());
      events::raise_draw_event();
      gui_->draw();
      for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
		::clear_shroud(t - teams_.begin() + 1);
      }
}

void playsingle_controller::recruit(){
      if (!browse_)
            menu_handler_.recruit(browse_, player_number_, mouse_handler_.get_last_hex());
}

void playsingle_controller::repeat_recruit(){
      if (!browse_)
            menu_handler_.repeat_recruit(player_number_, mouse_handler_.get_last_hex());
}

void playsingle_controller::recall(){
      if (!browse_)
            menu_handler_.recall(player_number_, mouse_handler_.get_last_hex());
}

void playsingle_controller::toggle_shroud_updates(){
      menu_handler_.toggle_shroud_updates(gui_->viewing_team()+1);
}

void playsingle_controller::update_shroud_now(){
      menu_handler_.update_shroud_now(gui_->viewing_team()+1);
}

void playsingle_controller::end_turn(){
      if (linger_)
            end_turn_ = true;
      else if (!browse_){
            browse_ = true;
            end_turn_ = menu_handler_.end_turn(player_number_);
            browse_ = end_turn_;
      }
}

void playsingle_controller::force_end_turn(){
      skip_next_turn_ = true;
      end_turn_ = true;
}

void playsingle_controller::check_end_level()
{
      if ((level_result_ == NONE) || linger_)
            return;
      throw end_level_exception(level_result_);
}

void playsingle_controller::rename_unit(){
      menu_handler_.rename_unit(mouse_handler_);
}

void playsingle_controller::create_unit(){
      menu_handler_.create_unit(mouse_handler_);
}

void playsingle_controller::change_side(){
      menu_handler_.change_side(mouse_handler_);
}

void playsingle_controller::label_terrain(bool team_only){
      menu_handler_.label_terrain(mouse_handler_, team_only);
}

void playsingle_controller::continue_move(){
      menu_handler_.continue_move(mouse_handler_, player_number_);
}

void playsingle_controller::add_waypoint(){
      if (!browse_)
            mouse_handler_.add_waypoint(mouse_handler_.get_last_hex());
}

void playsingle_controller::unit_hold_position(){
      if (!browse_)
            menu_handler_.unit_hold_position(mouse_handler_, player_number_);
}

void playsingle_controller::end_unit_turn(){
      if (!browse_)
            menu_handler_.end_unit_turn(mouse_handler_, player_number_);
}

void playsingle_controller::user_command(){
      menu_handler_.user_command();
}

void playsingle_controller::custom_command(){
      menu_handler_.custom_command(mouse_handler_, player_number_);
}

void playsingle_controller::ai_formula(){
      menu_handler_.ai_formula();
}

void playsingle_controller::clear_messages(){
      menu_handler_.clear_messages();
}

#ifdef USRCMD2
void playsingle_controller::user_command_2(){
      menu_handler_.user_command_2();
}

void playsingle_controller::user_command_3(){
      menu_handler_.user_command_3();
}
#endif

void playsingle_controller::report_victory(
      std::ostringstream &report, int player_gold, int remaining_gold,
      int finishing_bonus_per_turn, int turns_left, int finishing_bonus)
{
      end_level_data &end_level = get_end_level_data();
      report << _("Remaining gold: ")
               << remaining_gold << "\n";
      if(end_level.gold_bonus) {
            if (turns_left > -1) {
                  report << _("Early finish bonus: ")
                           << finishing_bonus_per_turn
                           << " " << _("per turn") << "\n"
                           << "<b>" << _("Turns finished early: ")
                           << turns_left << "</b>\n"
                           << _("Bonus: ")
                           << finishing_bonus << "\n";
            }
            report << _("Gold: ")
                   << (remaining_gold + finishing_bonus);
      }
      if (remaining_gold > 0) {
            report << '\n' << _("Carry over percentage: ") << end_level.carryover_percentage;
      }
      if(end_level.carryover_add) {
            report << "\n<b>" << _("Bonus Gold: ") << player_gold <<"</b>";
      } else {
            report << "\n<b>" << _("Retained Gold: ") << player_gold << "</b>";
      }

      std::string goldmsg;
      utils::string_map symbols;

      if (player_gold < 0) {
            symbols["gold"] = "0";
      } else {
            symbols["gold"] = lexical_cast_default<std::string>(player_gold);
      }

      // Note that both strings are the same in english, but some languages will
      // want to translate them differently.
      if(end_level.carryover_add) {
            goldmsg = vngettext(
                  "You will start the next scenario with $gold "
                  "on top of the defined minimum starting gold.",
                  "You will start the next scenario with $gold "
                  "on top of the defined minimum starting gold.",
                  player_gold, symbols);

      } else {
            goldmsg = vngettext(
                  "You will start the next scenario with $gold "
                  "or its defined minimum starting gold, "
                  "whichever is higher.",
                  "You will start the next scenario with $gold "
                  "or its defined minimum starting gold, "
                  "whichever is higher.",
                  player_gold, symbols);
      }

      // xgettext:no-c-format
      report << '\n' << goldmsg;
}

LEVEL_RESULT playsingle_controller::play_scenario(
      const config::const_child_itors &story, upload_log &log,
      bool skip_replay)
{
      LOG_NG << "in playsingle_controller::play_scenario()...\n";

      // Start music.
      foreach (const config &m, level_.child_range("music")) {
            sound::play_music_config(m);
      }
      sound::commit_music_changes();

      if(!skip_replay) {
            foreach (const config &s, story) {
                  show_storyscreen(*gui_, vconfig(s, true), level_["name"]);
            }
      }
      gui_->labels().read(level_);

      // Find a list of 'items' (i.e. overlays) on the level, and add them
      foreach (const config &overlay, level_.child_range("item"))
      {
            gui_->add_overlay(
                  map_location(overlay, resources::state_of_game),
                  overlay["image"], overlay["halo"], overlay["team_name"],
                  utils::string_bool(overlay["visible_in_fog"], true));
      }

      // Read sound sources
      assert(soundsources_manager_ != NULL);
      foreach (const config &s, level_.child_range("sound_source")) {
            soundsource::sourcespec spec(s);
            soundsources_manager_->add(spec);
      }

      set_victory_when_enemies_defeated(
            utils::string_bool(level_["victory_when_enemies_defeated"], true));
      end_level_data &end_level = get_end_level_data();
      end_level.carryover_percentage = lexical_cast_default<int>(
            level_["carryover_percentage"], game_config::gold_carryover_percentage);
      end_level.carryover_add = utils::string_bool(level_["carryover_add"]);

      LOG_NG << "entering try... " << (SDL_GetTicks() - ticks_) << "\n";
      try {
            // Log before prestart events: they do weird things.
            if (first_human_team_ != -1) { //sp logs
                  log.start(gamestate_, teams_[first_human_team_],
                              loading_game_ ? gamestate_.get_variable("turn_number") : "",
                              number_of_turns(), resources::game_map->write());
            } else { //ai vs. ai upload logs
                  log.start(gamestate_, resources::game_map->write());
            }

            fire_prestart(!loading_game_);
            init_gui();

            LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";

            fire_start(!loading_game_);
            gui_->recalculate_minimap();

            replaying_ = (recorder.at_end() == false);

            LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n";

            // Initialize countdown clock.
            std::vector<team>::iterator t;
            for(t = teams_.begin(); t != teams_.end(); ++t) {
                  if (gamestate_.mp_settings().mp_countdown && !loading_game_ ){
                        t->set_countdown_time(1000 * gamestate_.mp_settings().mp_countdown_init_time);
                  }
            }

            // if we loaded a save file in linger mode, skip to it.
            if (linger_) {
                  //determine the bonus gold handling for this scenario
                  const config end_cfg = level_.child_or_empty("endlevel");
                  end_level.carryover_percentage = lexical_cast_default<int>(
                        level_["carryover_percentage"],
                        game_config::gold_carryover_percentage);
                  end_level.carryover_add = utils::string_bool(level_["carryover_add"]);
                  end_level.gold_bonus = utils::string_bool(end_cfg["bonus"], true);
                  end_level.carryover_report = false;
                  throw end_level_exception(SKIP_TO_LINGER);
            }

            // Avoid autosaving after loading, but still
            // allow the first turn to have an autosave.
            bool save = !loading_game_;
            ai_testing::log_game_start();
            for(; ; first_player_ = 1) {
                  play_turn(save);
                  save = true;
            } //end for loop

      } catch(game::load_game_exception& lge) {
            // Loading a new game is effectively a quit.
            //
            if (lge.game != "") {
                  gamestate_ = game_state();
            }
            log.quit(turn());
            throw lge;
      } catch (end_level_exception &end_level_exn) {
            ai_testing::log_game_end();
            LEVEL_RESULT end_level_result = end_level_exn.result;
            if (!end_level.custom_endlevel_music.empty()) {
                  if (end_level_result == DEFEAT) {
                        set_defeat_music_list(end_level.custom_endlevel_music);
                  } else {
                        set_victory_music_list(end_level.custom_endlevel_music);
                  }
            }

            if (teams_.empty())
            {
                  //store persistent teams
                  gamestate_.snapshot = config();
                  store_recalls();

                  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)
            {
                  gamestate_.classification().completion = (end_level_exn.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"] = gamestate_.classification().completion;
                        network::send_data(cfg, 0, true);
                  } else {
                        gui2::show_transient_message(gui_->video(),_("Game Over"),
                                                      _("The game is over."));
                        return OBSERVER_END;
                  }
            }

            if (end_level_result == QUIT) {
                  log.quit(turn());
                  return QUIT;
            }
            else if (end_level_result == DEFEAT)
            {
                  gamestate_.classification().completion = "defeat";
                  log.defeat(turn());
                  game_events::fire("defeat");

                  if (!obs) {
                        const std::string& defeat_music = select_defeat_music();
                        if(defeat_music.empty() != true)
                              sound::play_music_once(defeat_music);

                        return DEFEAT;
                  } else {
                        return QUIT;
                  }
            }
            else if (end_level_result == VICTORY)
            {
                  gamestate_.classification().completion =
                        !end_level.linger_mode ? "running" : "victory";
                  game_events::fire("victory");

                  //
                  // Play victory music once all victory events
                  // are finished, if we aren't observers.
                  //
                  // Some scenario authors may use 'continue'
                  // result for something that is not story-wise
                  // a victory, so let them use [music] tags
                  // instead should they want special music.
                  //
                  if (!obs && end_level.linger_mode) {
                        const std::string& victory_music = select_victory_music();
                        if(victory_music.empty() != true)
                              sound::play_music_once(victory_music);
                  }
                  if (first_human_team_ != -1)
                        log.victory(turn(), teams_[first_human_team_].gold());

                  // Add all the units that survived the scenario.
                  LOG_NG << "Add units that survived the scenario to the recall list.\n";
                  for(unit_map::iterator un = units_.begin(); un != units_.end(); ++un) {

                        if(teams_[un->second.side()-1].persistent()) {
                              LOG_NG << "Added unit " << un->second.id() << ", " << un->second.name() << "\n";
                              un->second.new_turn();
                              un->second.new_scenario();
                              teams_[un->second.side()-1].recall_list().push_back(un->second);
                        }
                  }
                  //store all units that survived (recall list for the next scenario) in snapshot
                  gamestate_.snapshot = config();
                  store_recalls();
                  //store gold and report victory
                  store_gold(obs);
                  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
                  gamestate_.snapshot = config();
                  store_recalls();
                  store_gold();
                  return VICTORY;
            }
      } // end catch
      catch(network::error& e) {
            bool disconnect = false;
            if(e.socket) {
                  e.disconnect();
                  disconnect = true;
            }

            savegame::game_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
            /** @todo FIXME: remove the odd linebreak after the string freeze */
            save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game\ncannot continue. Do you want to save the game?"), gui::YES_NO);
            if(disconnect) {
                  throw network::error();
            } else {
                  return QUIT;
            }
      }

      return QUIT;
}

void playsingle_controller::play_turn(bool save)
{
      gui_->new_turn();
      gui_->invalidate_game_status();
      events::raise_draw_event();

      LOG_NG << "turn: " << turn() << "\n";

      if(non_interactive())
            std::cout << "Turn " << turn() << ":" << std::endl;


      for (player_number_ = first_player_; player_number_ <= int(teams_.size()); ++player_number_)
      {
            // If a side is empty skip over it.
            if (current_team().is_empty()) continue;
            try {
                  save_blocker blocker;
                  init_side(player_number_ - 1);
            } catch (end_turn_exception) {
                  if (current_team().is_network() == false) {
                        turn_info turn_data(player_number_, replay_sender_, undo_stack_);
                        recorder.end_turn();
                        turn_data.sync_network();
                  }
                  continue;
            }

            if (replaying_) {
                  LOG_NG << "doing replay " << player_number_ << "\n";
                  replaying_ = ::do_replay(player_number_);
                  LOG_NG << "result of replay: " << (replaying_?"true":"false") << "\n";
            } else {
                  // If a side is dead end the turn.
                  if (current_team().is_human() && side_units(units_, player_number_) == 0)
                  {
                        turn_info turn_data(player_number_, replay_sender_, undo_stack_);
                        recorder.end_turn();
                        turn_data.sync_network();
                        continue;
                  }
                  ai_testing::log_turn_start(player_number_);
                  play_side(player_number_, save);
            }

            finish_side_turn();

            if(non_interactive()) {
                  std::cout << " Player " << player_number_ << ": " <<
                        current_team().villages().size() << " Villages" <<
                        std::endl;
                  ai_testing::log_turn_end(player_number_);
            }

            check_victory();

            //if loading a savegame, network turns might not have reset this yet
            loading_game_ = false;
      }

      // Time has run out
      check_time_over();

      finish_turn();
}

void playsingle_controller::play_side(const unsigned int team_index, bool save)
{
      //check for team-specific items in the scenario
      gui_->parse_team_overlays();

      //flag used when we fallback from ai and give temporarily control to human
      bool temporary_human = false;
      do {
            // Although this flag is used only in this method,
            // it has to be a class member since derived classes
            // rely on it
            player_type_changed_ = false;
            if (!skip_next_turn_)
                  end_turn_ = false;


            statistics::reset_turn_stats(teams_[team_index - 1].save_id());

            if(current_team().is_human() || temporary_human) {
                  LOG_NG << "is human...\n";
                  try{
                        before_human_turn(save);
                        play_human_turn();
                        after_human_turn();
                  } catch(end_turn_exception& end_turn) {
                        if (end_turn.redo == team_index) {
                              player_type_changed_ = true;
                              // If new controller is not human,
                              // reset gui to prev human one
                              if (!teams_[team_index-1].is_human()) {
                                    browse_ = true;
                                    int t = find_human_team_before(team_index);
                                    if (t > 0) {
                                          gui_->set_team(t-1);
                                          gui_->recalculate_minimap();
                                          gui_->invalidate_all();
                                          gui_->draw(true,true);
                                    }
                              }
                        }
                  }

                  if(game_config::debug)
                        game_display::clear_debug_highlights();

                  LOG_NG << "human finished turn...\n";
            } else if(current_team().is_ai()) {
                  try {
                        play_ai_turn();
                  } catch(fallback_ai_to_human_exception&) {
                        //give control to human for the rest of this turn
                        player_type_changed_ = true;
                        temporary_human = true;
                  }
            }
      } while (player_type_changed_);
      // Keep looping if the type of a team (human/ai/networked)
      // has changed mid-turn
      skip_next_turn_ = false;
}

void playsingle_controller::before_human_turn(bool save)
{
      log_scope("player turn");
      browse_ = false;
      linger_ = false;

      gui_->set_team(player_number_ - 1);
      gui_->recalculate_minimap();
      gui_->invalidate_all();
      gui_->draw(true,true);

      ai::manager::raise_turn_started();

      if (save) {
            savegame::autosave_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
            save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);
      }

      if(preferences::turn_bell()) {
            sound::play_bell(game_config::sounds::turn_bell);
      }
}

void playsingle_controller::show_turn_dialog(){
      if(preferences::turn_dialog() && (level_result_ == NONE) ) {
            std::string message = _("It is now $name|'s turn");
            utils::string_map symbols;
            symbols["name"] = teams_[player_number_ - 1].current_player();
            message = utils::interpolate_variables_into_string(message, &symbols);
            gui2::show_transient_message(gui_->video(), "", message);
      }
}

void playsingle_controller::execute_gotos(){
      menu_handler_.execute_gotos(mouse_handler_, player_number_);
}

void playsingle_controller::play_human_turn() {
      show_turn_dialog();
      execute_gotos();

      gui_->enable_menu("endturn", true);
      while(!end_turn_) {
            play_slice();
            check_end_level();
            gui_->draw();
      }
}
struct set_completion
{
      set_completion(game_state& state, const std::string& completion) :
            state_(state), completion_(completion)
      {
      }
      ~set_completion()
      {
            state_.classification().completion = completion_;
      }
      private:
      game_state& state_;
      const std::string completion_;
};

void playsingle_controller::linger(upload_log& log)
{
      LOG_NG << "beginning end-of-scenario linger\n";
      browse_ = true;
      linger_ = true;

      // If we need to set the status depending on the completion state
      // the key to it is here.
      gui_->set_game_mode(game_display::LINGER_SP);

      // this is actually for after linger mode is over -- we don't
      // want to stay stuck in linger state when the *next* scenario
      // is over.
      set_completion setter(gamestate_,"running");

      // change the end-turn button text to its alternate label
      gui_->get_theme().refresh_title2("button-endturn", "title2");
      gui_->invalidate_theme();
      gui_->redraw_everything();

      // End all unit moves
      for (unit_map::iterator u = units_.begin(); u != units_.end(); ++u) {
            u->second.set_user_end_turn(true);
      }
      try {
            // Same logic as single-player human turn, but
            // *not* the same as multiplayer human turn.
            gui_->enable_menu("endturn", true);
            while(!end_turn_) {
                  // Reset the team number to make sure we're the right team.
                  player_number_ = first_player_;
                  play_slice();
                  gui_->draw();
            }
      } catch(game::load_game_exception& lge) {
            // Loading a new game is effectively a quit.
            if (lge.game != "") {
                  gamestate_ = game_state();
            }
            log.quit(turn());
            throw lge;
      }

      // revert the end-turn button text to its normal label
      gui_->get_theme().refresh_title2("button-endturn", "title");
      gui_->invalidate_theme();
      gui_->redraw_everything();
      gui_->set_game_mode(game_display::RUNNING);

      LOG_NG << "ending end-of-scenario linger\n";
}

void playsingle_controller::end_turn_record()
{
      if (!turn_over_)
      {
            turn_over_ = true;
            recorder.end_turn();
      }
}
void playsingle_controller::end_turn_record_unlock()
{
      turn_over_ = false;
}

void playsingle_controller::after_human_turn(){
      browse_ = true;
      end_turn_record();
      end_turn_record_unlock();
      menu_handler_.clear_undo_stack(player_number_);

      if(teams_[player_number_-1].uses_fog()) {
            // needed because currently fog is only recalculated when a hex is /un/covered
            recalculate_fog(player_number_);
      }

      gui_->set_route(NULL);
      gui_->unhighlight_reach();
}

void playsingle_controller::play_ai_turn(){
      LOG_NG << "is ai...\n";
      gui_->enable_menu("endturn", false);
      browse_ = true;
      gui_->recalculate_minimap();

      const cursor::setter cursor_setter(cursor::WAIT);

      turn_info turn_data(player_number_, replay_sender_, undo_stack_);

      try {
            ai::manager::play_turn(player_number_);
      } catch (end_turn_exception&) {
      }
      recorder.end_turn();
      turn_data.sync_network();

      gui_->recalculate_minimap();
	::clear_shroud(player_number_);
      gui_->invalidate_unit();
      gui_->invalidate_game_status();
      gui_->invalidate_all();
      gui_->draw();
      gui_->delay(100);
}

void playsingle_controller::handle_generic_event(const std::string& name){
      if (name == "ai_user_interact"){
            play_slice(false);
      }
      if (end_turn_){
            throw end_turn_exception();
      }
}

void playsingle_controller::check_time_over(){
      bool b = next_turn();
      if(!b) {

            LOG_NG << "firing time over event...\n";
            game_events::fire("time over");
            LOG_NG << "done firing time over event...\n";
            //if turns are added while handling 'time over' event
            if (tod_manager_.is_time_left()) {
                  return;
            }

            if(non_interactive()) {
                  std::cout << "time over (draw)\n";
                  ai_testing::log_draw();
            }

            check_end_level();
            throw end_level_exception(DEFEAT);
      }
}

void playsingle_controller::store_recalls() {
      std::set<std::string> side_ids;
      std::vector<team>::iterator i;
      for(i=teams_.begin(); i!=teams_.end(); ++i) {
            side_ids.insert(i->save_id());
            if (i->persistent()) {
                  config& new_side = gamestate_.snapshot.add_child("side");
                  new_side["save_id"] = i->save_id();
                  new_side["name"] = i->current_player();
                  std::stringstream can_recruit;
                  std::copy(i->recruits().begin(),i->recruits().end(),std::ostream_iterator<std::string>(can_recruit,","));
                  std::string can_recruit_str = can_recruit.str();
                  // Remove the trailing comma
                  if(can_recruit_str.empty() == false) {
                        can_recruit_str.resize(can_recruit_str.size()-1);
                  }
                  new_side["previous_recruits"] = can_recruit_str;
                  LOG_NG << "stored side in snapshot:\n" << new_side["save_id"] << std::endl;
                  //add the units of the recall list
                  foreach(const unit& u, i->recall_list()) {
                        config& new_unit = new_side.add_child("unit");
                        u.write(new_unit);
                  }
            }
      }
      //add any players from starting_pos that do not have a team in the current scenario
      foreach (const config* player_cfg, gamestate_.starting_pos.get_children("player")) {
            if (side_ids.count((*player_cfg)["save_id"]) == 0) {
                  LOG_NG << "stored inactive side in snapshot:\n" << (*player_cfg)["save_id"] << std::endl;
                  gamestate_.snapshot.add_child("side", (*player_cfg));
            }
      }
}

void playsingle_controller::store_gold(bool obs)
{
      bool has_next_scenario = !gamestate_.classification().next_scenario.empty() &&
            gamestate_.classification().next_scenario != "null";

      std::ostringstream report;
      std::string title;

      if (obs) {
            title = _("Scenario Report");
      } else {
            title = _("Victory");
            report << "<b>" << _("You have emerged victorious!") << "</b>\n\n";
      }

      int persistent_teams = 0;
      foreach (const team &t, teams_) {
            if (t.persistent()) ++persistent_teams;
      }

      end_level_data &end_level = get_end_level_data();

      if (persistent_teams > 0 && (has_next_scenario ||
            gamestate_.classification().campaign_type == "test"))
      {
            int finishing_bonus_per_turn =
                  map_.villages().size() * game_config::village_income +
                  game_config::base_income;
            int turns_left = std::max<int>(0, number_of_turns() - turn());
            int finishing_bonus = (end_level.gold_bonus && turns_left > -1) ?
                  finishing_bonus_per_turn * turns_left : 0;
            foreach (const team &t, teams_)
            {
                  if (!t.persistent()) continue;
                  int carryover_gold = ((t.gold() + finishing_bonus) * end_level.carryover_percentage) / 100;
                  config::child_itors side_range = gamestate_.snapshot.child_range("side");
                  config::child_iterator side_it = side_range.first;

                  // Check if this side already exists in the snapshot.
                  while (side_it != side_range.second) {
                        if ((*side_it)["save_id"] == t.save_id()) {
                              (*side_it)["gold"] = str_cast<int>(carryover_gold);
                              (*side_it)["gold_add"] = end_level.carryover_add ? "yes" : "no";
                              (*side_it)["colour"] = t.colour();
                              (*side_it)["current_player"] = t.current_player();
                              (*side_it)["name"] = t.name();
                              break;
                        }
                        ++side_it;
                  }

                  // If it doesn't, add a new child.
                  if (side_it == side_range.second) {
                        config &new_side = gamestate_.snapshot.add_child("side");
                        new_side["save_id"] = t.save_id();
                        new_side["gold"] = str_cast<int>(carryover_gold);
                        new_side["gold_add"] = end_level.carryover_add ? "yes" : "no";
                        new_side["colour"] = t.colour();
                        new_side["current_player"] = t.current_player();
                        new_side["name"] = t.name();
                  }

                  // Only show the report for ourselves.
                  if (!t.is_human()) continue;

                  if (persistent_teams > 1) {
                        report << "\n<b>" << t.current_player() << "</b>\n";
                  }

                  report_victory(report, carryover_gold, t.gold(), finishing_bonus_per_turn, turns_left, finishing_bonus);
            }
      }

      if (end_level.carryover_report) {
            gui2::show_transient_message(gui_->video(), title, report.str(), true);
      }
}

bool playsingle_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int index) const
{
      bool res = true;
      switch (command){
            case hotkey::HOTKEY_ADD_WAYPOINT:
            case hotkey::HOTKEY_UNIT_HOLD_POSITION:
            case hotkey::HOTKEY_END_UNIT_TURN:
            case hotkey::HOTKEY_RECRUIT:
            case hotkey::HOTKEY_REPEAT_RECRUIT:
            case hotkey::HOTKEY_RECALL:
                  return !browse_ && !linger_ && !events::commands_disabled;
            case hotkey::HOTKEY_ENDTURN:
                  return (!browse_ || linger_) && !events::commands_disabled;

            case hotkey::HOTKEY_DELAY_SHROUD:
                  return !linger_ && (teams_[gui_->viewing_team()].uses_fog() || teams_[gui_->viewing_team()].uses_shroud())
                  && !events::commands_disabled;
            case hotkey::HOTKEY_UPDATE_SHROUD:
                  return !linger_
                        && player_number_ == gui_->viewing_side()
                        && !events::commands_disabled
                        && teams_[gui_->viewing_team()].auto_shroud_updates() == false;

            // Commands we can only do if in debug mode
            case hotkey::HOTKEY_CREATE_UNIT:
            case hotkey::HOTKEY_CHANGE_SIDE:
                  return !events::commands_disabled && game_config::debug && map_.on_board(mouse_handler_.get_last_hex());

            case hotkey::HOTKEY_LABEL_TEAM_TERRAIN:
            case hotkey::HOTKEY_LABEL_TERRAIN:
                  res = !events::commands_disabled && map_.on_board(mouse_handler_.get_last_hex())
                        && !gui_->shrouded(mouse_handler_.get_last_hex())
                        && !is_observer();
                  break;

            case hotkey::HOTKEY_CONTINUE_MOVE: {
                  if(browse_ || events::commands_disabled)
                        return false;

                  if( (menu_handler_.current_unit(mouse_handler_) != units_.end())
                        && (menu_handler_.current_unit(mouse_handler_)->second.move_interrupted()))
                        return true;
                  const unit_map::const_iterator i = units_.find(mouse_handler_.get_selected_hex());
                  if (i == units_.end()) return false;
                  return i->second.move_interrupted();
            }
            default: return play_controller::can_execute_command(command, index);
      }
      return res;
}

Generated by  Doxygen 1.6.0   Back to index