00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "play_controller.hpp"
00023 #include "dialogs.hpp"
00024 #include "foreach.hpp"
00025 #include "game_events.hpp"
00026 #include "gettext.hpp"
00027 #include "halo.hpp"
00028 #include "loadscreen.hpp"
00029 #include "log.hpp"
00030 #include "pathfind/teleport.hpp"
00031 #include "resources.hpp"
00032 #include "savegame.hpp"
00033 #include "sound.hpp"
00034 #include "unit_id.hpp"
00035 #include "terrain_filter.hpp"
00036 #include "save_blocker.hpp"
00037 #include "preferences_display.hpp"
00038 #include "replay.hpp"
00039 #include "soundsource.hpp"
00040 #include "tooltips.hpp"
00041 #include "game_preferences.hpp"
00042 #include "wml_exception.hpp"
00043 #include "formula_string_utils.hpp"
00044 #include "ai/manager.hpp"
00045 #include "ai/testing.hpp"
00046 #include "whiteboard/manager.hpp"
00047 #include "scripting/lua.hpp"
00048
00049 static lg::log_domain log_engine("engine");
00050 #define LOG_NG LOG_STREAM(info, log_engine)
00051 #define DBG_NG LOG_STREAM(debug, log_engine)
00052
00053 static lg::log_domain log_display("display");
00054 #define ERR_DP LOG_STREAM(err, log_display)
00055
00056 static void clear_resources()
00057 {
00058 resources::game_map = NULL;
00059 resources::units = NULL;
00060 resources::teams = NULL;
00061 resources::state_of_game = NULL;
00062 resources::controller = NULL;
00063 resources::screen = NULL;
00064 resources::soundsources = NULL;
00065 resources::tod_manager = NULL;
00066 resources::whiteboard = NULL;
00067 resources::persist = NULL;
00068 }
00069
00070 play_controller::play_controller(const config& level, game_state& state_of_game,
00071 int ticks, int num_turns, const config& game_config, CVideo& video,
00072 bool skip_replay) :
00073 controller_base(ticks, game_config, video),
00074 observer(),
00075 savegame_config(),
00076 animation_cache(),
00077 prefs_disp_manager_(),
00078 tooltips_manager_(),
00079 events_manager_(),
00080 halo_manager_(),
00081 labels_manager_(),
00082 help_manager_(&game_config, &map_),
00083 mouse_handler_(NULL, teams_, units_, map_, tod_manager_),
00084 menu_handler_(NULL, units_, teams_, level, map_, game_config, tod_manager_, state_of_game),
00085 soundsources_manager_(),
00086 tod_manager_(level, num_turns),
00087 pathfind_manager_(),
00088 persist_(),
00089 gui_(),
00090 statistics_context_(level["name"]),
00091 level_(level),
00092 teams_(),
00093 gamestate_(state_of_game),
00094 map_(game_config, level),
00095 units_(),
00096 undo_stack_(),
00097 redo_stack_(),
00098 whiteboard_manager_(),
00099 xp_mod_(level["experience_modifier"].to_int(100)),
00100 loading_game_(level["playing_team"].empty() == false),
00101 first_human_team_(-1),
00102 player_number_(1),
00103 first_player_(level_["playing_team"].to_int() + 1),
00104 start_turn_(tod_manager_.turn()),
00105 is_host_(true),
00106 skip_replay_(skip_replay),
00107 linger_(false),
00108 it_is_a_new_turn_(true),
00109 init_side_done_(false),
00110 savenames_(),
00111 wml_commands_(),
00112 victory_when_enemies_defeated_(true),
00113 end_level_data_(),
00114 victory_music_(),
00115 defeat_music_()
00116 {
00117 resources::game_map = &map_;
00118 resources::units = &units_;
00119 resources::teams = &teams_;
00120 resources::state_of_game = &gamestate_;
00121 resources::controller = this;
00122 resources::tod_manager = &tod_manager_;
00123 resources::undo_stack = &undo_stack_;
00124 resources::redo_stack = &redo_stack_;
00125 resources::persist = &persist_;
00126 persist_.start_transaction();
00127
00128
00129 set_victory_music_list(level_["victory_music"]);
00130 set_defeat_music_list(level_["defeat_music"]);
00131
00132 game_config::add_color_info(level);
00133 hotkey::deactivate_all_scopes();
00134 hotkey::set_scope_active(hotkey::SCOPE_GENERAL);
00135 hotkey::set_scope_active(hotkey::SCOPE_GAME);
00136 try {
00137 init(video);
00138 } catch (...) {
00139 clear_resources();
00140 throw;
00141 }
00142 }
00143
00144 play_controller::~play_controller()
00145 {
00146 clear_resources();
00147 resources::tunnels = NULL;
00148 }
00149
00150 void play_controller::init(CVideo& video){
00151 util::scoped_resource<loadscreen::global_loadscreen_manager*, util::delete_item> scoped_loadscreen_manager;
00152 loadscreen::global_loadscreen_manager* loadscreen_manager = loadscreen::global_loadscreen_manager::get();
00153 if (!loadscreen_manager)
00154 {
00155 scoped_loadscreen_manager.assign(new loadscreen::global_loadscreen_manager(video));
00156 loadscreen_manager = scoped_loadscreen_manager.get();
00157 }
00158
00159 loadscreen::start_stage("load level");
00160
00161
00162 if(recorder.empty()) {
00163 recorder.add_start();
00164 } else {
00165 recorder.pre_replay();
00166 }
00167 recorder.set_skip(false);
00168
00169 bool snapshot = level_["snapshot"].to_bool();
00170
00171 if (level_["modify_placing"].to_bool()) {
00172 LOG_NG << "modifying placing...\n";
00173 place_sides_in_preferred_locations();
00174 }
00175
00176 foreach (const config &t, level_.child_range("time_area")) {
00177 tod_manager_.add_time_area(t);
00178 }
00179
00180 LOG_NG << "initialized teams... " << (SDL_GetTicks() - ticks_) << "\n";
00181 loadscreen::start_stage("init teams");
00182
00183 resources::teams->resize(level_.child_count("side"));
00184
00185
00186
00187
00188 events_manager_.reset(new game_events::manager(level_));
00189
00190 std::set<std::string> seen_save_ids;
00191
00192 std::vector<team_builder_ptr> team_builders;
00193
00194 int team_num = 0;
00195 foreach (const config &side, level_.child_range("side"))
00196 {
00197 std::string save_id = get_unique_saveid(side, seen_save_ids);
00198 seen_save_ids.insert(save_id);
00199 if (first_human_team_ == -1) {
00200 const std::string &controller = side["controller"];
00201 if (controller == preferences::client_type() &&
00202 side["id"] == preferences::login()) {
00203 first_human_team_ = team_num;
00204 } else if (controller == "human") {
00205 first_human_team_ = team_num;
00206 }
00207 }
00208 team_builder_ptr tb_ptr = gamestate_.create_team_builder(side,
00209 save_id, teams_, level_, map_, units_, snapshot);
00210 ++team_num;
00211 gamestate_.build_team_stage_one(tb_ptr);
00212 team_builders.push_back(tb_ptr);
00213 }
00214
00215 foreach (team_builder_ptr tb_ptr, team_builders)
00216 {
00217 gamestate_.build_team_stage_two(tb_ptr);
00218 }
00219
00220
00221 if (teams_.empty()) end_level_data_.linger_mode = false;
00222
00223 LOG_NG << "loading units..." << (SDL_GetTicks() - ticks_) << "\n";
00224 loadscreen::start_stage("load units");
00225 preferences::encounter_recruitable_units(teams_);
00226 preferences::encounter_start_units(units_);
00227 preferences::encounter_recallable_units(teams_);
00228 preferences::encounter_map_terrain(map_);
00229
00230
00231 LOG_NG << "initializing theme... " << (SDL_GetTicks() - ticks_) << '\n';
00232 loadscreen::start_stage("init theme");
00233 const config &theme_cfg = get_theme(game_config_, level_["theme"]);
00234
00235 LOG_NG << "building terrain rules... " << (SDL_GetTicks() - ticks_) << '\n';
00236 loadscreen::start_stage("build terrain");
00237 gui_.reset(new game_display(units_, video, map_, tod_manager_, teams_, theme_cfg, level_));
00238 if (!gui_->video().faked()) {
00239 if (gamestate_.mp_settings().mp_countdown)
00240 gui_->get_theme().modify_label("time-icon", _ ("time left for current turn"));
00241 else
00242 gui_->get_theme().modify_label("time-icon", _ ("current local time"));
00243 }
00244
00245 loadscreen::start_stage("init display");
00246 mouse_handler_.set_gui(gui_.get());
00247 menu_handler_.set_gui(gui_.get());
00248 resources::screen = gui_.get();
00249
00250 LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks_) << "\n";
00251
00252 if(first_human_team_ != -1) {
00253 gui_->set_team(first_human_team_);
00254 }
00255 else if (is_observer())
00256 {
00257
00258
00259
00260 size_t i;
00261 for (i=0;i < teams_.size();++i)
00262 {
00263 if (!teams_[i].get_disallow_observers())
00264 {
00265 gui_->set_team(i);
00266 }
00267 }
00268 }
00269
00270 browse_ = true;
00271
00272 init_managers();
00273
00274 if (const config &era_cfg = level_.child("era")) {
00275 game_events::add_events(era_cfg.child_range("event"), "era_events");
00276 }
00277
00278 loadscreen::global_loadscreen->start_stage("start game");
00279 loadscreen_manager->reset();
00280 }
00281
00282 void play_controller::init_managers(){
00283 LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks_) << "\n";
00284 prefs_disp_manager_.reset(new preferences::display_manager(gui_.get()));
00285 tooltips_manager_.reset(new tooltips::manager(gui_->video()));
00286 soundsources_manager_.reset(new soundsource::manager(*gui_));
00287 pathfind_manager_.reset(new pathfind::manager(level_));
00288 whiteboard_manager_.reset(new wb::manager());
00289
00290 resources::soundsources = soundsources_manager_.get();
00291 resources::tunnels = pathfind_manager_.get();
00292 resources::whiteboard = whiteboard_manager_.get();
00293
00294 halo_manager_.reset(new halo::manager(*gui_));
00295 LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks_) << "\n";
00296 }
00297
00298 static int placing_score(const config& side, const gamemap& map, const map_location& pos)
00299 {
00300 int positions = 0, liked = 0;
00301 const t_translation::t_list terrain = t_translation::read_list(side["terrain_liked"]);
00302
00303 for(int i = pos.x-8; i != pos.x+8; ++i) {
00304 for(int j = pos.y-8; j != pos.y+8; ++j) {
00305 const map_location pos(i,j);
00306 if(map.on_board(pos)) {
00307 ++positions;
00308 if(std::count(terrain.begin(),terrain.end(),map[pos])) {
00309 ++liked;
00310 }
00311 }
00312 }
00313 }
00314
00315 return (100*liked)/positions;
00316 }
00317
00318 struct placing_info {
00319
00320 placing_info() :
00321 side(0),
00322 score(0),
00323 pos()
00324 {
00325 }
00326
00327 int side, score;
00328 map_location pos;
00329 };
00330
00331 static bool operator<(const placing_info& a, const placing_info& b) { return a.score > b.score; }
00332
00333 void play_controller::place_sides_in_preferred_locations()
00334 {
00335 std::vector<placing_info> placings;
00336
00337 int num_pos = map_.num_valid_starting_positions();
00338
00339 int side_num = 1;
00340 foreach (const config &side, level_.child_range("side"))
00341 {
00342 for(int p = 1; p <= num_pos; ++p) {
00343 const map_location& pos = map_.starting_position(p);
00344 int score = placing_score(side, map_, pos);
00345 placing_info obj;
00346 obj.side = side_num;
00347 obj.score = score;
00348 obj.pos = pos;
00349 placings.push_back(obj);
00350 }
00351 ++side_num;
00352 }
00353
00354 std::sort(placings.begin(),placings.end());
00355 std::set<int> placed;
00356 std::set<map_location> positions_taken;
00357
00358 for (std::vector<placing_info>::const_iterator i = placings.begin(); i != placings.end() && int(placed.size()) != side_num - 1; ++i) {
00359 if(placed.count(i->side) == 0 && positions_taken.count(i->pos) == 0) {
00360 placed.insert(i->side);
00361 positions_taken.insert(i->pos);
00362 map_.set_starting_position(i->side,i->pos);
00363 LOG_NG << "placing side " << i->side << " at " << i->pos << '\n';
00364 }
00365 }
00366 }
00367
00368 void play_controller::objectives(){
00369 menu_handler_.objectives(gui_->viewing_team()+1);
00370 }
00371
00372 void play_controller::show_statistics(){
00373 menu_handler_.show_statistics(gui_->viewing_team()+1);
00374 }
00375
00376 void play_controller::unit_list(){
00377 menu_handler_.unit_list();
00378 }
00379
00380 void play_controller::status_table(){
00381 menu_handler_.status_table();
00382 }
00383
00384 void play_controller::save_game(){
00385 if(save_blocker::try_block()) {
00386 save_blocker::save_unblocker unblocker;
00387 savegame::game_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
00388 save.save_game_interactive(gui_->video(), "", gui::OK_CANCEL);
00389 } else {
00390 save_blocker::on_unblock(this,&play_controller::save_game);
00391 }
00392 }
00393
00394 void play_controller::save_replay(){
00395 if(save_blocker::try_block()) {
00396 save_blocker::save_unblocker unblocker;
00397 savegame::replay_savegame save(gamestate_, preferences::compress_saves());
00398 save.save_game_interactive(gui_->video(), "", gui::OK_CANCEL);
00399 } else {
00400 save_blocker::on_unblock(this,&play_controller::save_replay);
00401 }
00402 }
00403
00404 void play_controller::save_map(){
00405 if(save_blocker::try_block()) {
00406 save_blocker::save_unblocker unblocker;
00407 menu_handler_.save_map();
00408 } else {
00409 save_blocker::on_unblock(this,&play_controller::save_map);
00410 }
00411 }
00412
00413 void play_controller::load_game(){
00414 savegame::loadgame load(*gui_, game_config_, gamestate_);
00415 load.load_game();
00416 }
00417
00418 void play_controller::preferences(){
00419 menu_handler_.preferences();
00420 }
00421
00422 void play_controller::left_mouse_click(){
00423 int x = gui_->get_location_x(gui_->mouseover_hex());
00424 int y = gui_->get_location_y(gui_->mouseover_hex());
00425
00426 SDL_MouseButtonEvent event;
00427
00428 event.button = 1;
00429 event.x = x + 30;
00430 event.y = y + 30;
00431 event.which = 0;
00432 event.state = SDL_PRESSED;
00433
00434 mouse_handler_.mouse_press(event, false);
00435 }
00436
00437 void play_controller::right_mouse_click(){
00438 int x = gui_->get_location_x(gui_->mouseover_hex());
00439 int y = gui_->get_location_y(gui_->mouseover_hex());
00440
00441 SDL_MouseButtonEvent event;
00442
00443 event.button = 3;
00444 event.x = x + 30;
00445 event.y = y + 30;
00446 event.which = 0;
00447 event.state = SDL_PRESSED;
00448
00449 mouse_handler_.mouse_press(event, true);
00450 }
00451
00452
00453 void play_controller::cycle_units(){
00454 mouse_handler_.cycle_units(browse_);
00455 }
00456
00457 void play_controller::cycle_back_units(){
00458 mouse_handler_.cycle_back_units(browse_);
00459 }
00460
00461 void play_controller::show_chat_log(){
00462 menu_handler_.show_chat_log();
00463 }
00464
00465 void play_controller::show_help(){
00466 menu_handler_.show_help();
00467 }
00468
00469 void play_controller::undo(){
00470
00471 mouse_handler_.deselect_hex();
00472 menu_handler_.undo(player_number_);
00473 }
00474
00475 void play_controller::redo(){
00476
00477 mouse_handler_.deselect_hex();
00478 menu_handler_.redo(player_number_);
00479 }
00480
00481 void play_controller::show_enemy_moves(bool ignore_units){
00482 menu_handler_.show_enemy_moves(ignore_units, player_number_);
00483 }
00484
00485 void play_controller::goto_leader(){
00486 menu_handler_.goto_leader(player_number_);
00487 }
00488
00489 void play_controller::unit_description(){
00490 menu_handler_.unit_description();
00491 }
00492
00493 void play_controller::toggle_ellipses(){
00494 menu_handler_.toggle_ellipses();
00495 }
00496
00497 void play_controller::toggle_grid(){
00498 menu_handler_.toggle_grid();
00499 }
00500
00501 void play_controller::search(){
00502 menu_handler_.search();
00503 }
00504
00505 void play_controller::fire_prestart(bool execute)
00506 {
00507
00508 resources::state_of_game->set_phase(game_state::PRELOAD);
00509 resources::lua_kernel->initialize();
00510 game_events::fire("preload");
00511
00512
00513
00514 if (execute){
00515 update_locker lock_display(gui_->video());
00516 resources::state_of_game->set_phase(game_state::PRESTART);
00517 game_events::fire("prestart");
00518 check_end_level();
00519
00520 start_turn_ = turn();
00521 }
00522 }
00523
00524 void play_controller::fire_start(bool execute){
00525 if(execute) {
00526 resources::state_of_game->set_phase(game_state::START);
00527 game_events::fire("start");
00528 check_end_level();
00529
00530 start_turn_ = turn();
00531 gamestate_.get_variable("turn_number") = int(start_turn_);
00532 } else {
00533 it_is_a_new_turn_ = false;
00534 }
00535 resources::state_of_game->set_phase(game_state::PLAY);
00536 }
00537
00538 void play_controller::init_gui(){
00539 gui_->begin_game();
00540 gui_->update_tod();
00541
00542 for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
00543 clear_shroud(t - teams_.begin() + 1);
00544 }
00545 }
00546
00547 void play_controller::init_side(const unsigned int team_index, bool is_replay){
00548 log_scope("player turn");
00549 init_side_done_ = false;
00550
00551 mouse_handler_.set_side(team_index + 1);
00552
00553
00554 if (is_observer() && !current_team().get_disallow_observers()) {
00555 gui_->set_team(size_t(team_index));
00556 }
00557 gui_->set_playing_team(size_t(team_index));
00558
00559 gamestate_.get_variable("side_number") = player_number_;
00560 gamestate_.last_selected = map_location::null_location;
00561
00562 maybe_do_init_side(team_index, is_replay);
00563 }
00564
00565
00566
00567
00568 void play_controller::maybe_do_init_side(const unsigned int team_index, bool is_replay) {
00569
00570
00571
00572
00573
00574 if (is_replay || init_side_done_ || !current_team().is_local()) {
00575 return;
00576 }
00577
00578 if (!loading_game_) recorder.init_side();
00579
00580 do_init_side(team_index, is_replay);
00581 }
00582
00583
00584
00585
00586 void play_controller::do_init_side(const unsigned int team_index, bool is_replay) {
00587 log_scope("player turn");
00588 team& current_team = teams_[team_index];
00589
00590 const std::string turn_num = str_cast(turn());
00591 const std::string side_num = str_cast(team_index + 1);
00592
00593
00594 if (!loading_game_) {
00595 if(it_is_a_new_turn_)
00596 {
00597 game_events::fire("turn " + turn_num);
00598 game_events::fire("new turn");
00599 it_is_a_new_turn_ = false;
00600 }
00601
00602 game_events::fire("side turn");
00603 game_events::fire("side " + side_num + " turn");
00604 game_events::fire("side turn " + turn_num);
00605 game_events::fire("side " + side_num + " turn " + turn_num);
00606 }
00607
00608 if(current_team.is_human() && !is_replay) {
00609 update_gui_to_player(player_number_ - 1);
00610 }
00611
00612
00613
00614
00615 if (!loading_game_ && turn() > 1) {
00616 for(unit_map::iterator i = units_.begin(); i != units_.end(); ++i) {
00617 if (i->side() == player_number_) {
00618 i->new_turn();
00619 }
00620 }
00621
00622 current_team.new_turn();
00623
00624
00625
00626
00627 int expense = side_upkeep(player_number_) -
00628 current_team.support();
00629 if(expense > 0) {
00630 current_team.spend_gold(expense);
00631 }
00632
00633 calculate_healing(player_number_, !skip_replay_);
00634 reset_resting(units_, player_number_);
00635 }
00636
00637 if (!loading_game_) {
00638 game_events::fire("turn refresh");
00639 game_events::fire("side " + side_num + " turn refresh");
00640 game_events::fire("turn " + turn_num + " refresh");
00641 game_events::fire("side " + side_num + " turn " + turn_num + " refresh");
00642 }
00643
00644 const time_of_day &tod = tod_manager_.get_time_of_day();
00645
00646 if (int(team_index) + 1 == first_player_)
00647 sound::play_sound(tod.sounds, sound::SOUND_SOURCES);
00648
00649 if (!recorder.is_skipping()){
00650 clear_shroud(team_index + 1, !loading_game_);
00651 gui_->invalidate_all();
00652 }
00653
00654 if (!recorder.is_skipping() && !skip_replay_ && current_team.get_scroll_to_leader()){
00655 gui_->scroll_to_leader(units_, player_number_,game_display::ONSCREEN,false);
00656 }
00657 loading_game_ = false;
00658 init_side_done_ = true;
00659
00660 resources::whiteboard->on_init_side();
00661 }
00662
00663
00664 config play_controller::to_config() const
00665 {
00666 config cfg;
00667
00668 cfg.merge_attributes(level_);
00669
00670 for(std::vector<team>::const_iterator t = teams_.begin(); t != teams_.end(); ++t) {
00671 int side_num = t - teams_.begin() + 1;
00672
00673 config& side = cfg.add_child("side");
00674 t->write(side);
00675 side["no_leader"] = true;
00676 side["side"] = str_cast(side_num);
00677
00678 if (!linger_){
00679
00680 for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
00681 if (i->side() == side_num) {
00682 config& u = side.add_child("unit");
00683 i->get_location().write(u);
00684 i->write(u);
00685 }
00686 }
00687 }
00688
00689 {
00690 for(std::vector<unit>::const_iterator j = t->recall_list().begin();
00691 j != t->recall_list().end(); ++j) {
00692 config& u = side.add_child("unit");
00693 j->write(u);
00694 }
00695 }
00696 }
00697
00698 cfg.merge_with(tod_manager_.to_config());
00699
00700 if(linger_) {
00701 config endlevel;
00702 end_level_data_.write(endlevel);
00703 cfg.add_child("endlevel", endlevel);
00704 }
00705
00706
00707 foreach (const config &tg, level_.child_range("terrain_graphics")) {
00708 cfg.add_child("terrain_graphics", tg);
00709 }
00710
00711
00712
00713 config& map = cfg.add_child("map");
00714 map_.write(map);
00715
00716 cfg.merge_with(pathfind_manager_->to_config());
00717
00718 config display;
00719 gui_->write(display);
00720 cfg.add_child("display", display);
00721
00722 return cfg;
00723 }
00724
00725 void play_controller::finish_side_turn(){
00726
00727 resources::whiteboard->on_finish_side_turn(player_number_);
00728
00729 for(unit_map::iterator uit = units_.begin(); uit != units_.end(); ++uit) {
00730 if (uit->side() == player_number_)
00731 uit->end_turn();
00732 }
00733
00734 const std::string turn_num = str_cast(turn());
00735 const std::string side_num = str_cast(player_number_);
00736 game_events::fire("side turn end");
00737 game_events::fire("side "+ side_num + " turn end");
00738 game_events::fire("side turn " + turn_num + " end");
00739 game_events::fire("side " + side_num + " turn " + turn_num + " end");
00740
00741
00742
00743 if(current_team().copy_ally_shroud()) {
00744 gui_->recalculate_minimap();
00745 gui_->invalidate_all();
00746 }
00747
00748 mouse_handler_.deselect_hex();
00749 n_unit::id_manager::instance().reset_fake();
00750 game_events::pump();
00751 }
00752
00753 void play_controller::finish_turn()
00754 {
00755 const std::string turn_num = str_cast(turn());
00756 game_events::fire("turn end");
00757 game_events::fire("turn " + turn_num + " end");
00758 }
00759
00760 bool play_controller::enemies_visible() const
00761 {
00762
00763 if(current_team().uses_fog() == false && current_team().uses_shroud() == false)
00764 return true;
00765
00766
00767 for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u)
00768 if (current_team().is_enemy(u->side()) && !gui_->fogged(u->get_location()))
00769 return true;
00770
00771 return false;
00772 }
00773
00774 bool play_controller::execute_command(hotkey::HOTKEY_COMMAND command, int index)
00775 {
00776 if(index >= 0) {
00777 unsigned i = static_cast<unsigned>(index);
00778 if(i < savenames_.size() && !savenames_[i].empty()) {
00779
00780 throw game::load_game_exception(savenames_[i],false,false,false,"");
00781
00782 } else if (i < wml_commands_.size() && wml_commands_[i] != NULL) {
00783 if(gamestate_.last_selected.valid() && wml_commands_[i]->needs_select) {
00784 recorder.add_event("select", gamestate_.last_selected);
00785 }
00786 map_location const& menu_hex = mouse_handler_.get_last_hex();
00787 recorder.add_event(wml_commands_[i]->name, menu_hex);
00788 if(game_events::fire(wml_commands_[i]->name, menu_hex)) {
00789
00790 apply_shroud_changes(undo_stack_, player_number_);
00791 undo_stack_.clear();
00792 }
00793 return true;
00794 }
00795 }
00796 return command_executor::execute_command(command, index);
00797 }
00798
00799 bool play_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int index) const
00800 {
00801 if(index >= 0) {
00802 unsigned i = static_cast<unsigned>(index);
00803 if((i < savenames_.size() && !savenames_[i].empty())
00804 || (i < wml_commands_.size() && wml_commands_[i] != NULL)) {
00805 return true;
00806 }
00807 }
00808 switch(command) {
00809
00810
00811 case hotkey::HOTKEY_LEADER:
00812 case hotkey::HOTKEY_CYCLE_UNITS:
00813 case hotkey::HOTKEY_CYCLE_BACK_UNITS:
00814 case hotkey::HOTKEY_ZOOM_IN:
00815 case hotkey::HOTKEY_ZOOM_OUT:
00816 case hotkey::HOTKEY_ZOOM_DEFAULT:
00817 case hotkey::HOTKEY_FULLSCREEN:
00818 case hotkey::HOTKEY_SCREENSHOT:
00819 case hotkey::HOTKEY_MAP_SCREENSHOT:
00820 case hotkey::HOTKEY_ACCELERATED:
00821 case hotkey::HOTKEY_SAVE_MAP:
00822 case hotkey::HOTKEY_TOGGLE_ELLIPSES:
00823 case hotkey::HOTKEY_TOGGLE_GRID:
00824 case hotkey::HOTKEY_MOUSE_SCROLL:
00825 case hotkey::HOTKEY_ANIMATE_MAP:
00826 case hotkey::HOTKEY_STATUS_TABLE:
00827 case hotkey::HOTKEY_MUTE:
00828 case hotkey::HOTKEY_PREFERENCES:
00829 case hotkey::HOTKEY_OBJECTIVES:
00830 case hotkey::HOTKEY_UNIT_LIST:
00831 case hotkey::HOTKEY_STATISTICS:
00832 case hotkey::HOTKEY_QUIT_GAME:
00833 case hotkey::HOTKEY_SEARCH:
00834 case hotkey::HOTKEY_HELP:
00835 case hotkey::HOTKEY_USER_CMD:
00836 case hotkey::HOTKEY_CUSTOM_CMD:
00837 case hotkey::HOTKEY_AI_FORMULA:
00838 case hotkey::HOTKEY_CLEAR_MSG:
00839 case hotkey::HOTKEY_LEFT_MOUSE_CLICK:
00840 case hotkey::HOTKEY_RIGHT_MOUSE_CLICK:
00841 return true;
00842
00843
00844 case hotkey::HOTKEY_SAVE_GAME:
00845 case hotkey::HOTKEY_SAVE_REPLAY:
00846 return !events::commands_disabled;
00847
00848 case hotkey::HOTKEY_SHOW_ENEMY_MOVES:
00849 case hotkey::HOTKEY_BEST_ENEMY_MOVES:
00850 return !linger_ && enemies_visible();
00851
00852 case hotkey::HOTKEY_LOAD_GAME:
00853 return network::nconnections() == 0;
00854
00855 case hotkey::HOTKEY_CHAT_LOG:
00856 return network::nconnections() > 0;
00857
00858 case hotkey::HOTKEY_REDO:
00859 return !linger_ && !redo_stack_.empty() && !events::commands_disabled && !browse_;
00860 case hotkey::HOTKEY_UNDO:
00861 return !linger_ && !undo_stack_.empty() && !events::commands_disabled && !browse_;
00862
00863 case hotkey::HOTKEY_UNIT_DESCRIPTION:
00864 return menu_handler_.current_unit() != units_.end();
00865
00866 case hotkey::HOTKEY_RENAME_UNIT:
00867 return !events::commands_disabled &&
00868 menu_handler_.current_unit() != units_.end() &&
00869 !(menu_handler_.current_unit()->unrenamable()) &&
00870 menu_handler_.current_unit()->side() == gui_->viewing_side() &&
00871 teams_[menu_handler_.current_unit()->side() - 1].is_human();
00872
00873 default:
00874 return false;
00875 }
00876 }
00877
00878 void play_controller::enter_textbox()
00879 {
00880 if(menu_handler_.get_textbox().active() == false) {
00881 return;
00882 }
00883
00884 const std::string str = menu_handler_.get_textbox().box()->text();
00885 const unsigned int team_num = player_number_;
00886 events::mouse_handler& mousehandler = mouse_handler_;
00887
00888 switch(menu_handler_.get_textbox().mode()) {
00889 case gui::TEXTBOX_SEARCH:
00890 menu_handler_.do_search(str);
00891 menu_handler_.get_textbox().close(*gui_);
00892 break;
00893 case gui::TEXTBOX_MESSAGE:
00894 menu_handler_.do_speak();
00895 menu_handler_.get_textbox().close(*gui_);
00896 break;
00897 case gui::TEXTBOX_COMMAND:
00898 menu_handler_.get_textbox().close(*gui_);
00899 menu_handler_.do_command(str);
00900 break;
00901 case gui::TEXTBOX_AI:
00902 menu_handler_.get_textbox().close(*gui_);
00903 menu_handler_.do_ai_formula(str, team_num, mousehandler);
00904 break;
00905 default:
00906 menu_handler_.get_textbox().close(*gui_);
00907 ERR_DP << "unknown textbox mode\n";
00908 }
00909
00910 }
00911
00912 void play_controller::tab()
00913 {
00914 gui::TEXTBOX_MODE mode = menu_handler_.get_textbox().mode();
00915
00916 std::set<std::string> dictionary;
00917 switch(mode) {
00918 case gui::TEXTBOX_SEARCH:
00919 {
00920 foreach (const unit &u, units_){
00921 const map_location& loc = u.get_location();
00922 if(!gui_->fogged(loc) &&
00923 !(teams_[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc)))
00924 dictionary.insert(u.name());
00925 }
00926
00927 break;
00928 }
00929 case gui::TEXTBOX_COMMAND:
00930 {
00931 std::vector<std::string> commands = menu_handler_.get_commands_list();
00932 dictionary.insert(commands.begin(), commands.end());
00933
00934 }
00935 case gui::TEXTBOX_MESSAGE:
00936 {
00937 foreach(const team& t, teams_) {
00938 if(!t.is_empty())
00939 dictionary.insert(t.current_player());
00940 }
00941
00942
00943 foreach(const std::string& o, gui_->observers()){
00944 dictionary.insert(o);
00945 }
00946
00947
00948 dictionary.erase(preferences::login());
00949 break;
00950 }
00951
00952 default:
00953 ERR_DP << "unknown textbox mode\n";
00954 }
00955
00956 menu_handler_.get_textbox().tab(dictionary);
00957 }
00958
00959
00960 std::string play_controller::get_unique_saveid(const config& cfg, std::set<std::string>& seen_save_ids)
00961 {
00962 std::string save_id = cfg["save_id"];
00963
00964 if(save_id.empty()) {
00965 save_id = cfg["id"].str();
00966 }
00967
00968 if(save_id.empty()) {
00969 save_id="Unknown";
00970 }
00971
00972
00973 while(seen_save_ids.count(save_id)) {
00974 save_id += "_";
00975 }
00976
00977 return save_id;
00978 }
00979
00980 team& play_controller::current_team()
00981 {
00982 assert(player_number_ > 0 && player_number_ <= int(teams_.size()));
00983 return teams_[player_number_-1];
00984 }
00985
00986 const team& play_controller::current_team() const
00987 {
00988 assert(player_number_ > 0 && player_number_ <= int(teams_.size()));
00989 return teams_[player_number_-1];
00990 }
00991
00992 int play_controller::find_human_team_before(const size_t team_num) const
00993 {
00994 if (team_num > teams_.size())
00995 return -2;
00996
00997 int human_side = -2;
00998 for (int i = team_num-2; i > -1; --i) {
00999 if (teams_[i].is_human()) {
01000 human_side = i;
01001 break;
01002 }
01003 }
01004 if (human_side == -2) {
01005 for (size_t i = teams_.size()-1; i > team_num-1; --i) {
01006 if (teams_[i].is_human()) {
01007 human_side = i;
01008 break;
01009 }
01010 }
01011 }
01012 return human_side+1;
01013 }
01014
01015 void play_controller::slice_before_scroll() {
01016 soundsources_manager_->update();
01017 }
01018
01019 events::mouse_handler& play_controller::get_mouse_handler_base() {
01020 return mouse_handler_;
01021 }
01022
01023 game_display& play_controller::get_display() {
01024 return *gui_;
01025 }
01026
01027 bool play_controller::have_keyboard_focus()
01028 {
01029 return !menu_handler_.get_textbox().active();
01030 }
01031
01032 void play_controller::process_focus_keydown_event(const SDL_Event& event)
01033 {
01034 if(event.key.keysym.sym == SDLK_ESCAPE) {
01035 menu_handler_.get_textbox().close(*gui_);
01036 } else if(event.key.keysym.sym == SDLK_TAB) {
01037 tab();
01038 } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
01039 enter_textbox();
01040 }
01041 }
01042
01043 void play_controller::process_keydown_event(const SDL_Event& event) {
01044 if (event.key.keysym.sym == SDLK_TAB) {
01045 whiteboard_manager_->set_invert_behavior(true);
01046 }
01047 }
01048
01049 void play_controller::process_keyup_event(const SDL_Event& event) {
01050
01051
01052 if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '7') {
01053 const int new_path_turns = (event.type == SDL_KEYDOWN) ?
01054 event.key.keysym.sym - '1' : 0;
01055
01056 if(new_path_turns != mouse_handler_.get_path_turns()) {
01057 mouse_handler_.set_path_turns(new_path_turns);
01058
01059 const unit_map::iterator u = mouse_handler_.selected_unit();
01060
01061 if(u != units_.end()) {
01062
01063 unit_movement_resetter move_reset(*u, u->side() != player_number_);
01064
01065 mouse_handler_.set_current_paths(pathfind::paths(map_, units_, *u,
01066 teams_,false,true, teams_[gui_->viewing_team()],
01067 mouse_handler_.get_path_turns()));
01068
01069 gui_->highlight_reach(mouse_handler_.current_paths());
01070 }
01071 }
01072 } else if (event.key.keysym.sym == SDLK_TAB) {
01073 static CKey keys;
01074 if (!keys[SDLK_TAB]) {
01075 whiteboard_manager_->set_invert_behavior(false);
01076 }
01077 }
01078 }
01079
01080 void play_controller::post_mouse_press(const SDL_Event& )
01081 {
01082 if (mouse_handler_.get_undo()){
01083 mouse_handler_.set_undo(false);
01084 menu_handler_.undo(player_number_);
01085 }
01086 }
01087
01088 static void trim_items(std::vector<std::string>& newitems) {
01089 if (newitems.size() > 5) {
01090 std::vector<std::string> subitems;
01091 subitems.push_back(newitems[0]);
01092 subitems.push_back(newitems[1]);
01093 subitems.push_back(newitems[newitems.size() / 3]);
01094 subitems.push_back(newitems[newitems.size() * 2 / 3]);
01095 subitems.push_back(newitems.back());
01096 newitems = subitems;
01097 }
01098 }
01099
01100 void play_controller::expand_autosaves(std::vector<std::string>& items)
01101 {
01102 savenames_.clear();
01103 for (unsigned int i = 0; i < items.size(); ++i) {
01104 if (items[i] == "AUTOSAVES") {
01105 items.erase(items.begin() + i);
01106 std::vector<std::string> newitems;
01107 std::vector<std::string> newsaves;
01108 for (unsigned int turn = this->turn(); turn != 0; turn--) {
01109 std::string name = gamestate_.classification().label + "-" + _("Auto-Save") + lexical_cast<std::string>(turn);
01110 if (savegame::save_game_exists(name, preferences::compress_saves())) {
01111 if(preferences::compress_saves()) {
01112 newsaves.push_back(name + ".gz");
01113 } else {
01114 newsaves.push_back(name);
01115 }
01116 newitems.push_back(_("Back to Turn ") + lexical_cast<std::string>(turn));
01117 }
01118 }
01119
01120 const std::string& start_name = gamestate_.classification().label;
01121 if(savegame::save_game_exists(start_name, preferences::compress_saves())) {
01122 if(preferences::compress_saves()) {
01123 newsaves.push_back(start_name + ".gz");
01124 } else {
01125 newsaves.push_back(start_name);
01126 }
01127 newitems.push_back(_("Back to Start"));
01128 }
01129
01130
01131
01132 trim_items(newitems);
01133 trim_items(newsaves);
01134
01135 items.insert(items.begin()+i, newitems.begin(), newitems.end());
01136 savenames_.insert(savenames_.end(), newsaves.begin(), newsaves.end());
01137 break;
01138 }
01139 savenames_.push_back("");
01140 }
01141 }
01142
01143 void play_controller::expand_wml_commands(std::vector<std::string>& items)
01144 {
01145 wml_commands_.clear();
01146 for (unsigned int i = 0; i < items.size(); ++i) {
01147 if (items[i] == "wml") {
01148 items.erase(items.begin() + i);
01149 std::map<std::string, wml_menu_item*>& gs_wmi = gamestate_.wml_menu_items;
01150 if(gs_wmi.empty())
01151 break;
01152 std::vector<std::string> newitems;
01153
01154 const map_location& hex = mouse_handler_.get_last_hex();
01155 gamestate_.get_variable("x1") = hex.x + 1;
01156 gamestate_.get_variable("y1") = hex.y + 1;
01157 scoped_xy_unit highlighted_unit("unit", hex.x, hex.y, units_);
01158
01159 std::map<std::string, wml_menu_item*>::iterator itor;
01160 for (itor = gs_wmi.begin(); itor != gs_wmi.end()
01161 && newitems.size() < MAX_WML_COMMANDS; ++itor) {
01162 config& show_if = itor->second->show_if;
01163 config filter_location = itor->second->filter_location;
01164 if ((show_if.empty()
01165 || game_events::conditional_passed(vconfig(show_if)))
01166 && (filter_location.empty()
01167 || terrain_filter(vconfig(filter_location), units_)(hex))
01168 && (!itor->second->needs_select
01169 || gamestate_.last_selected.valid()))
01170 {
01171 wml_commands_.push_back(itor->second);
01172 std::string newitem = itor->second->description;
01173
01174
01175 newitem.push_back(' ');
01176 newitems.push_back(newitem);
01177 }
01178 }
01179 items.insert(items.begin()+i, newitems.begin(), newitems.end());
01180 break;
01181 }
01182 wml_commands_.push_back(NULL);
01183 }
01184 }
01185
01186 void play_controller::show_menu(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu)
01187 {
01188 std::vector<std::string> items = items_arg;
01189 hotkey::HOTKEY_COMMAND command;
01190 std::vector<std::string>::iterator i = items.begin();
01191 while(i != items.end()) {
01192 if (*i == "AUTOSAVES") {
01193
01194 command = hotkey::HOTKEY_LOAD_GAME;
01195 } else {
01196 command = hotkey::get_hotkey(*i).get_id();
01197 }
01198
01199 if(*i == "wml") {
01200 if(!context_menu || gui_->viewing_team() != gui_->playing_team()
01201 || events::commands_disabled || !teams_[gui_->viewing_team()].is_human()
01202 || (linger_ && !game_config::debug)){
01203 i = items.erase(i);
01204 continue;
01205 }
01206
01207 } else if(!can_execute_command(command)
01208 || (context_menu && !in_context_menu(command))) {
01209 i = items.erase(i);
01210 continue;
01211 }
01212 ++i;
01213 }
01214
01215
01216 expand_autosaves(items);
01217 expand_wml_commands(items);
01218
01219 if(items.empty())
01220 return;
01221
01222 command_executor::show_menu(items, xloc, yloc, context_menu, *gui_);
01223 }
01224
01225 bool play_controller::in_context_menu(hotkey::HOTKEY_COMMAND command) const
01226 {
01227 switch(command) {
01228
01229 case hotkey::HOTKEY_RECRUIT:
01230 case hotkey::HOTKEY_REPEAT_RECRUIT:
01231 case hotkey::HOTKEY_RECALL: {
01232 wb::future_map future;
01233
01234
01235 for(unit_map::const_iterator leader = units_.begin();
01236 leader != units_.end();++leader) {
01237 if (leader->can_recruit() &&
01238 leader->side() == resources::screen->viewing_side() &&
01239 can_recruit_on(map_, leader->get_location(), mouse_handler_.get_last_hex()))
01240 return true;
01241 }
01242 return false;
01243 }
01244 default:
01245 return true;
01246 }
01247 }
01248
01249 std::string play_controller::get_action_image(hotkey::HOTKEY_COMMAND command, int index) const
01250 {
01251 if(index >= 0 && index < static_cast<int>(wml_commands_.size())) {
01252 wml_menu_item* const& wmi = wml_commands_[index];
01253 if(wmi != NULL) {
01254 return wmi->image.empty() ? game_config::images::wml_menu : wmi->image;
01255 }
01256 }
01257 return command_executor::get_action_image(command, index);
01258 }
01259
01260 hotkey::ACTION_STATE play_controller::get_action_state(hotkey::HOTKEY_COMMAND command, int ) const
01261 {
01262 switch(command) {
01263 case hotkey::HOTKEY_DELAY_SHROUD:
01264 return teams_[gui_->viewing_team()].auto_shroud_updates() ? hotkey::ACTION_OFF : hotkey::ACTION_ON;
01265 default:
01266 return hotkey::ACTION_STATELESS;
01267 }
01268 }
01269
01270 namespace {
01271 static const std::string empty_str = "";
01272 }
01273
01274 const std::string& play_controller::select_victory_music() const
01275 {
01276 if(victory_music_.empty())
01277 return empty_str;
01278 return victory_music_[rand() % victory_music_.size()];
01279 }
01280
01281 const std::string& play_controller::select_defeat_music() const
01282 {
01283 if(defeat_music_.empty())
01284 return empty_str;
01285 return defeat_music_[rand() % defeat_music_.size()];
01286 }
01287
01288
01289 void play_controller::set_victory_music_list(const std::string& list)
01290 {
01291 victory_music_ = utils::split(list);
01292 if(victory_music_.empty())
01293 victory_music_ = utils::split(game_config::default_victory_music);
01294 }
01295
01296 void play_controller::set_defeat_music_list(const std::string& list)
01297 {
01298 defeat_music_ = utils::split(list);
01299 if(defeat_music_.empty())
01300 defeat_music_ = utils::split(game_config::default_defeat_music);
01301 }
01302
01303 void play_controller::check_victory()
01304 {
01305 check_end_level();
01306
01307 std::vector<unsigned> seen_leaders;
01308 for (unit_map::const_iterator i = units_.begin(),
01309 i_end = units_.end(); i != i_end; ++i)
01310 {
01311 if (i->can_recruit()) {
01312 DBG_NG << "seen leader for side " << i->side() << "\n";
01313 seen_leaders.push_back(i->side());
01314 }
01315 }
01316
01317
01318 for (std::vector<team>::iterator tm_beg = teams_.begin(), tm = tm_beg,
01319 tm_end = teams_.end(); tm != tm_end; ++tm)
01320 {
01321 if (std::find(seen_leaders.begin(), seen_leaders.end(), tm - tm_beg + 1) == seen_leaders.end()) {
01322 tm->clear_villages();
01323
01324
01325 gui_->invalidate_all();
01326 }
01327 }
01328
01329 bool found_player = false;
01330
01331 for (size_t n = 0; n != seen_leaders.size(); ++n) {
01332 size_t side = seen_leaders[n] - 1;
01333
01334 for (size_t m = n +1 ; m != seen_leaders.size(); ++m) {
01335 if (teams_[side].is_enemy(seen_leaders[m])) {
01336 return;
01337 }
01338 }
01339
01340 if (teams_[side].is_human()) {
01341 found_player = true;
01342 }
01343 }
01344
01345 if (found_player) {
01346 game_events::fire("enemies defeated");
01347 check_end_level();
01348 }
01349
01350 if (!victory_when_enemies_defeated_ && (found_player || is_observer())) {
01351
01352 return;
01353 }
01354
01355 if (non_interactive()) {
01356 std::cout << "winner: ";
01357 foreach (unsigned l, seen_leaders) {
01358 std::string ai = ai::manager::get_active_ai_identifier_for_side(l);
01359 if (ai.empty()) ai = "default ai";
01360 std::cout << l << " (using " << ai << ") ";
01361 }
01362 std::cout << '\n';
01363 ai_testing::log_victory(seen_leaders);
01364 }
01365
01366 DBG_NG << "throwing end level exception...\n";
01367 throw end_level_exception(found_player ? VICTORY : DEFEAT);
01368 }
01369
01370 void play_controller::process_oos(const std::string& msg) const
01371 {
01372 if (game_config::ignore_replay_errors) return;
01373
01374 std::stringstream message;
01375 message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
01376 message << "\n\n" << _("Error details:") << "\n\n" << msg;
01377
01378 savegame::oos_savegame save(to_config());
01379 save.save_game_interactive(resources::screen->video(), message.str(), gui::YES_NO);
01380 }
01381
01382 void play_controller::update_gui_to_player(const int team_index, const bool observe)
01383 {
01384 gui_->set_team(team_index, observe);
01385 gui_->recalculate_minimap();
01386 gui_->invalidate_all();
01387 gui_->draw(true,true);
01388 }
01389