00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "global.hpp"
00018
00019 #include "mouse_events.hpp"
00020
00021 #include "actions.hpp"
00022 #include "attack_prediction_display.hpp"
00023 #include "dialogs.hpp"
00024 #include "foreach.hpp"
00025 #include "game_end_exceptions.hpp"
00026 #include "game_events.hpp"
00027 #include "gettext.hpp"
00028 #include "gui/dialogs/unit_attack.hpp"
00029 #include "gui/widgets/settings.hpp"
00030 #include "gui/dialogs/transient_message.hpp"
00031 #include "gui/widgets/window.hpp"
00032 #include "language.hpp"
00033 #include "log.hpp"
00034 #include "map.hpp"
00035 #include "marked-up_text.hpp"
00036 #include "menu_events.hpp"
00037 #include "pathfind/teleport.hpp"
00038 #include "play_controller.hpp"
00039 #include "sound.hpp"
00040 #include "replay.hpp"
00041 #include "resources.hpp"
00042 #include "rng.hpp"
00043 #include "tod_manager.hpp"
00044 #include "wml_separators.hpp"
00045 #include "whiteboard/manager.hpp"
00046
00047 #include <boost/bind.hpp>
00048
00049 static lg::log_domain log_engine("engine");
00050 #define ERR_NG LOG_STREAM(err, log_engine)
00051 #define LOG_NG LOG_STREAM(info, log_engine)
00052
00053 namespace events{
00054
00055
00056 mouse_handler::mouse_handler(game_display* gui, std::vector<team>& teams,
00057 unit_map& units, gamemap& map, tod_manager& tod_mng) :
00058 mouse_handler_base(),
00059 map_(map),
00060 gui_(gui),
00061 teams_(teams),
00062 units_(units),
00063 tod_manager_(tod_mng),
00064 previous_hex_(),
00065 previous_free_hex_(),
00066 selected_hex_(),
00067 next_unit_(),
00068 current_route_(),
00069 current_paths_(),
00070 enemy_paths_(false),
00071 path_turns_(0),
00072 side_num_(1),
00073 undo_(false),
00074 over_route_(false),
00075 reachmap_invalid_(false),
00076 show_partial_move_(false)
00077 {
00078 singleton_ = this;
00079 }
00080
00081 mouse_handler::~mouse_handler()
00082 {
00083 rand_rng::clear_new_seed_callback();
00084 singleton_ = NULL;
00085 }
00086
00087 void mouse_handler::set_side(int side_number)
00088 {
00089 side_num_ = side_number;
00090 }
00091
00092 int mouse_handler::drag_threshold() const
00093 {
00094 return 14;
00095 }
00096
00097 void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update, map_location new_hex)
00098 {
00099
00100
00101
00102
00103
00104 SDL_GetMouseState(&x,&y);
00105
00106 if (mouse_handler_base::mouse_motion_default(x, y, update)) return;
00107
00108 if (new_hex == map_location::null_location)
00109 new_hex = gui().hex_clicked_on(x,y);
00110
00111 if(new_hex != last_hex_) {
00112 update = true;
00113 if (last_hex_.valid()) {
00114
00115 previous_hex_ = last_hex_;
00116
00117 {
00118 wb::future_map_if_active raii;
00119 if (last_hex_ == selected_hex_ || find_unit(last_hex_) == units_.end()) {
00120 previous_free_hex_ = last_hex_;
00121 }
00122 }
00123 }
00124 last_hex_ = new_hex;
00125 }
00126
00127
00128 if (reachmap_invalid_) update = true;
00129
00130 if (update) {
00131 if (reachmap_invalid_) {
00132 reachmap_invalid_ = false;
00133 if (!current_paths_.destinations.empty() && !show_partial_move_) {
00134 bool selected_hex_has_unit;
00135 {
00136 wb::future_map_if_active planned_unit_map;
00137 selected_hex_has_unit = find_unit(selected_hex_) != units_.end();
00138 }
00139 if(selected_hex_.valid() && selected_hex_has_unit ) {
00140
00141 select_hex(selected_hex_, true);
00142 }
00143
00144 }
00145 }
00146
00147
00148
00149 if(new_hex.valid() == false) {
00150 current_route_.steps.clear();
00151 gui().set_route(NULL);
00152 resources::whiteboard->erase_temp_move();
00153 }
00154
00155 if(enemy_paths_) {
00156 enemy_paths_ = false;
00157 current_paths_ = pathfind::paths();
00158 gui().unhighlight_reach();
00159 } else if(over_route_) {
00160 over_route_ = false;
00161 current_route_.steps.clear();
00162 gui().set_route(NULL);
00163 resources::whiteboard->erase_temp_move();
00164 }
00165
00166 gui().highlight_hex(new_hex);
00167 resources::whiteboard->on_mouseover_change(new_hex);
00168
00169 unit_map::iterator selected_unit;
00170 unit_map::iterator mouseover_unit;
00171 map_location attack_from;
00172
00173 {
00174 wb::future_map_if_active planned_unit_map;
00175 selected_unit = find_unit(selected_hex_);
00176 mouseover_unit = find_unit(new_hex);
00177
00178
00179 attack_from = current_unit_attacks_from(new_hex);
00180
00181
00182
00183
00184
00185 if (cursor::get() != cursor::WAIT) {
00186 if (selected_unit != units_.end() &&
00187 selected_unit->side() == side_num_ &&
00188 !selected_unit->incapacitated() && !browse)
00189 {
00190 if (attack_from.valid()) {
00191 cursor::set(dragging_started_ ? cursor::ATTACK_DRAG : cursor::ATTACK);
00192 }
00193 else if (mouseover_unit==units_.end() &&
00194 current_paths_.destinations.contains(new_hex))
00195 {
00196 cursor::set(dragging_started_ ? cursor::MOVE_DRAG : cursor::MOVE);
00197 } else {
00198
00199 cursor::set(cursor::NORMAL);
00200 }
00201 } else {
00202
00203 cursor::set(cursor::NORMAL);
00204 }
00205 }
00206 }
00207
00208
00209 if (attack_from.valid() && (!browse || resources::whiteboard->is_active())) {
00210 gui().set_attack_indicator(attack_from, new_hex);
00211 } else {
00212 gui().clear_attack_indicator();
00213 }
00214
00215 unit* un;
00216
00217
00218
00219 map_location dest;
00220 unit_map::const_iterator dest_un;
00221 {
00222 wb::future_map_if_active raii;
00223 if (attack_from.valid()) {
00224 dest = attack_from;
00225 dest_un = find_unit(dest);
00226 } else {
00227 dest = new_hex;
00228 dest_un = find_unit(new_hex);
00229 }
00230
00231 if(dest == selected_hex_ || dest_un != units_.end()) {
00232 current_route_.steps.clear();
00233 gui().set_route(NULL);
00234 resources::whiteboard->erase_temp_move();
00235 }
00236 else if (!current_paths_.destinations.empty() &&
00237 map_.on_board(selected_hex_) && map_.on_board(new_hex))
00238 {
00239 if (selected_unit != units_.end() && !selected_unit->incapacitated()) {
00240
00241 current_route_ = get_route(&*selected_unit, dest, viewing_team());
00242
00243 resources::whiteboard->create_temp_move();
00244
00245 if(!browse) {
00246 gui().set_route(¤t_route_);
00247 }
00248 }
00249 }
00250
00251 unit_map::iterator iter = mouseover_unit;
00252 if (iter != units_.end())
00253 un = &*iter;
00254 else
00255 un = NULL;
00256 }
00257
00258 if (un && current_paths_.destinations.empty() &&
00259 !gui().fogged(un->get_location()))
00260 {
00261 if (un->side() != side_num_) {
00262
00263
00264
00265 unit_movement_resetter move_reset(*un);
00266
00267
00268 {
00269 wb::future_map_if_active raii;
00270 current_paths_ = pathfind::paths(map_,units_,*un,teams_,
00271 false,true,viewing_team(),path_turns_);
00272 }
00273
00274 gui().highlight_reach(current_paths_);
00275 enemy_paths_ = true;
00276 } else {
00277
00278 const map_location go_to = un->get_goto();
00279 if(map_.on_board(go_to)) {
00280 pathfind::marked_route route;
00281 {
00282 wb::future_map_if_active raii;
00283 route = get_route(un, go_to, current_team());
00284 }
00285 gui().set_route(&route);
00286 }
00287 over_route_ = true;
00288 }
00289 }
00290 }
00291 }
00292
00293 unit_map::iterator mouse_handler::selected_unit()
00294 {
00295 unit_map::iterator res = find_unit(selected_hex_);
00296 if(res != units_.end()) {
00297 return res;
00298 } else {
00299 return find_unit(last_hex_);
00300 }
00301 }
00302
00303 unit_map::iterator mouse_handler::find_unit(const map_location& hex)
00304 {
00305 unit_map::iterator it = find_visible_unit(hex, viewing_team());
00306 if (it.valid())
00307 return it;
00308 else
00309 return resources::units->end();
00310 }
00311
00312 unit_map::const_iterator mouse_handler::find_unit(const map_location& hex) const
00313 {
00314 return find_visible_unit(hex, viewing_team());
00315 }
00316
00317 map_location mouse_handler::current_unit_attacks_from(const map_location& loc) const
00318 {
00319 if(loc == selected_hex_)
00320 return map_location();
00321
00322 bool wb_active = resources::whiteboard->is_active();
00323
00324 {
00325
00326
00327
00328 const unit_map::const_iterator source_unit = find_unit(selected_hex_);
00329 bool source_eligible = (source_unit != units_.end());
00330 if (!source_eligible) return map_location();
00331
00332
00333 source_eligible &= source_unit->side() == resources::screen->viewing_side();
00334 if (!source_eligible) return map_location();
00335
00336
00337
00338
00339 if(!wb_active) {
00340 source_eligible &= resources::screen->viewing_side() == resources::controller->current_side();
00341 if (!source_eligible) return map_location();
00342 }
00343
00344
00345 source_eligible &= source_unit->attacks_left() != 0;
00346 if (!source_eligible) return map_location();
00347
00348
00349
00350
00351 team const& viewing_team = (*resources::teams)[resources::screen->viewing_team()];
00352
00353
00354 const unit_map::const_iterator target_unit = find_unit(loc);
00355 bool target_eligible = (target_unit != units_.end());
00356 if (!target_eligible) return map_location();
00357
00358
00359 target_eligible &= viewing_team.is_enemy(target_unit->side());
00360 if (!target_eligible) return map_location();
00361
00362
00363 assert(source_unit->side() != target_unit->side());
00364
00365 target_eligible &= !target_unit->incapacitated();
00366 if (!target_eligible) return map_location();
00367 }
00368
00369 const map_location::DIRECTION preferred = loc.get_relative_dir(previous_hex_);
00370 const map_location::DIRECTION second_preferred = loc.get_relative_dir(previous_free_hex_);
00371
00372 int best_rating = 100;
00373 map_location res;
00374 map_location adj[6];
00375 get_adjacent_tiles(loc,adj);
00376
00377 for(size_t n = 0; n != 6; ++n) {
00378 if(map_.on_board(adj[n]) == false) {
00379 continue;
00380 }
00381
00382 if(adj[n] != selected_hex_ && find_unit(adj[n]) != units_.end()) {
00383 continue;
00384 }
00385
00386 if (current_paths_.destinations.contains(adj[n]))
00387 {
00388 static const size_t NDIRECTIONS = map_location::NDIRECTIONS;
00389 unsigned int difference = abs(int(preferred - n));
00390 if(difference > NDIRECTIONS/2) {
00391 difference = NDIRECTIONS - difference;
00392 }
00393 unsigned int second_difference = abs(int(second_preferred - n));
00394 if(second_difference > NDIRECTIONS/2) {
00395 second_difference = NDIRECTIONS - second_difference;
00396 }
00397 const int rating = difference * 2 + (second_difference > difference);
00398 if(rating < best_rating || res.valid() == false) {
00399 best_rating = rating;
00400 res = adj[n];
00401 }
00402 }
00403 }
00404
00405 return res;
00406 }
00407
00408 pathfind::marked_route mouse_handler::get_route(unit* un, map_location go_to, team &team)
00409 {
00410
00411 const pathfind::shortest_path_calculator calc(*un, team, units_, teams_, map_);
00412
00413 pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(*un, viewing_team());
00414
00415 pathfind::plain_route route;
00416
00417 route = pathfind::a_star_search(un->get_location(), go_to, 10000.0, &calc, map_.w(), map_.h(), &allowed_teleports);
00418
00419 return mark_route(route);
00420 }
00421
00422 void mouse_handler::mouse_press(const SDL_MouseButtonEvent& event, const bool browse)
00423 {
00424 mouse_handler_base::mouse_press(event, browse);
00425 }
00426
00427 bool mouse_handler::right_click_show_menu(int x, int y, const bool browse)
00428 {
00429
00430
00431 unit_map::iterator unit;
00432 {
00433 wb::future_map_if_active raii;
00434 unit = find_unit(selected_hex_);
00435 }
00436 if (selected_hex_.valid() && unit != units_.end()) {
00437 select_hex(map_location(), browse);
00438 return false;
00439 } else {
00440 return point_in_rect(x, y, gui().map_area());
00441 }
00442 }
00443
00444 bool mouse_handler::left_click(int x, int y, const bool browse)
00445 {
00446 undo_ = false;
00447 if (mouse_handler_base::left_click(x, y, browse)) return false;
00448
00449
00450
00451 wb::whiteboard_lock wb_lock = resources::whiteboard->get_activation_state_lock();
00452
00453
00454
00455 map_location hex = last_hex_;
00456
00457 unit_map::iterator u;
00458 unit_map::iterator clicked_u;
00459 map_location src;
00460 pathfind::paths orig_paths;
00461 map_location attack_from;
00462 {
00463 wb::future_map_if_active planned_unit_map;
00464 u = find_unit(selected_hex_);
00465
00466
00467
00468 if (u != units_.end() && !browse && selected_hex_ == hex && u->side() == side_num_) {
00469 u->set_goto(map_location());
00470 }
00471
00472 clicked_u = find_unit(hex);
00473
00474 src = selected_hex_;
00475 orig_paths = current_paths_;
00476 attack_from = current_unit_attacks_from(hex);
00477 }
00478
00479
00480 if((!browse || resources::whiteboard->is_active()) && !commands_disabled && attack_from.valid()) {
00481
00482 if (((u.valid() && u->side() == side_num_) || resources::whiteboard->is_active()) && clicked_u.valid() ) {
00483 if (attack_from == selected_hex_) {
00484 int choice = -1;
00485 { wb::future_map_if_active planned_unit_map;
00486 choice = show_attack_dialog(attack_from, clicked_u->get_location());
00487 }
00488 if (choice >=0 ) {
00489 if (resources::whiteboard->is_active()) {
00490 save_whiteboard_attack(attack_from, clicked_u->get_location(), choice);
00491 } else {
00492 attack_enemy(u->get_location(), clicked_u->get_location(), choice);
00493 }
00494 }
00495 return false;
00496 }
00497 else {
00498
00499 int choice = -1;
00500
00501 { wb::future_map_if_active planned_unit_map;
00502
00503 pathfind::paths::dest_vect::const_iterator itor =
00504 current_paths_.destinations.find(attack_from);
00505 if(itor == current_paths_.destinations.end()) {
00506
00507
00508 return false;
00509 }
00510
00511 int move_left_dst = itor->move_left;
00512 int move_left_src = u->movement_left();
00513 u->set_movement(move_left_dst);
00514
00515
00516 {
00517 temporary_unit_mover temp_mover(units_, src, attack_from);
00518
00519 choice = show_attack_dialog(attack_from, clicked_u->get_location());
00520
00521 }
00522
00523 u = units_.find(src);
00524 u->set_movement(move_left_src);
00525 u->set_standing();
00526
00527 if (choice < 0) {
00528
00529 return false;
00530 }
00531 }
00532
00533 if (resources::whiteboard->is_active()) {
00534 save_whiteboard_attack(attack_from, hex, choice);
00535 } else {
00536
00537 int side = u->side();
00538
00539 std::set<map_location> adj_enemies = get_adj_enemies(attack_from, side);
00540
00541
00542
00543 if(!move_unit_along_current_route(false)) {
00544
00545
00546
00547 return false;
00548 }
00549
00550
00551 if(get_adj_enemies(attack_from, side) != adj_enemies)
00552 return false;
00553
00554 attack_enemy(attack_from, hex, choice);
00555 }
00556 return false;
00557 }
00558 }
00559 }
00560
00561 else if((!browse || resources::whiteboard->is_active()) && !commands_disabled &&
00562 selected_hex_.valid() && selected_hex_ != hex &&
00563 u != units_.end() && u.valid() &&
00564 (u->side() == side_num_ || resources::whiteboard->is_active()) &&
00565 clicked_u == units_.end() &&
00566 !current_route_.steps.empty() &&
00567 current_route_.steps.front() == selected_hex_) {
00568
00569
00570 if (resources::whiteboard->is_active()) {
00571
00572 selected_hex_ = map_location();
00573 gui().select_hex(map_location());
00574 gui().clear_attack_indicator();
00575 gui().set_route(NULL);
00576 show_partial_move_ = false;
00577 gui().unhighlight_reach();
00578 current_paths_ = pathfind::paths();
00579 current_route_.steps.clear();
00580
00581 resources::whiteboard->save_temp_move();
00582
00583
00584 } else {
00585
00586
00587 if (resources::whiteboard->unit_has_actions(&*u)) {
00588 return false;
00589 }
00590
00591 move_unit_along_current_route(current_team().auto_shroud_updates());
00592
00593
00594
00595 if (selected_hex_ != src) {
00596 select_hex(selected_hex_, browse);
00597 }
00598 }
00599 return false;
00600 } else {
00601
00602
00603 select_hex(hex, browse);
00604 }
00605 return false;
00606
00607 }
00608
00609 void mouse_handler::select_hex(const map_location& hex, const bool browse, const bool highlight, const bool fire_event) {
00610 selected_hex_ = hex;
00611 gui().select_hex(hex);
00612 gui().clear_attack_indicator();
00613 gui().set_route(NULL);
00614 show_partial_move_ = false;
00615
00616 wb::future_map_if_active planned_unit_map;
00617
00618 unit_map::iterator u = find_unit(hex);
00619
00620 if (hex.valid() && u != units_.end() && u.valid() && !u->get_hidden()) {
00621
00622 next_unit_ = u->get_location();
00623
00624 {
00625 current_paths_ = pathfind::paths(map_, units_, *u, teams_,
00626 false, true, viewing_team(), path_turns_);
00627 }
00628 if(highlight) {
00629 show_attack_options(u);
00630 gui().highlight_reach(current_paths_);
00631 }
00632
00633
00634 enemy_paths_ = false;
00635 gui().set_route(NULL);
00636
00637
00638 if ((!commands_disabled || resources::whiteboard->is_active()) && u->side() == gui().viewing_side()) {
00639 if (!(browse || resources::whiteboard->unit_has_actions(&*u)))
00640 {
00641 sound::play_UI_sound("select-unit.wav");
00642 u->set_selecting();
00643 if(fire_event) {
00644
00645 wb::real_map srum;
00646 game_events::fire("select", hex);
00647
00648 }
00649 }
00650 }
00651
00652 } else {
00653 gui().unhighlight_reach();
00654 current_paths_ = pathfind::paths();
00655 current_route_.steps.clear();
00656 resources::whiteboard->on_deselect_hex();
00657 }
00658 }
00659
00660 void mouse_handler::deselect_hex() {
00661 select_hex(map_location(), true);
00662 }
00663
00664 bool mouse_handler::move_unit_along_current_route(bool check_shroud)
00665 {
00666
00667 gui().set_route(NULL);
00668 gui().unhighlight_reach();
00669
00670
00671 selected_hex_ = map_location();
00672 gui().select_hex(map_location());
00673
00674 bool finished_moves = move_unit_along_route(current_route_, &next_unit_, check_shroud);
00675
00676
00677 current_paths_ = pathfind::paths();
00678 current_route_.steps.clear();
00679
00680 return finished_moves;
00681 }
00682
00683 bool mouse_handler::move_unit_along_route(pathfind::marked_route const& route, map_location* next_unit, bool check_shroud, bool* sighted_result)
00684 {
00685 const std::vector<map_location> steps = route.steps;
00686 if(steps.empty()) {
00687 return false;
00688 }
00689
00690
00691
00692 {
00693 unit_map::const_iterator const u = units_.find(steps.front());
00694
00695 if (u != units_.end()
00696 && u->can_recruit()
00697 && u->side() == gui().viewing_side()
00698 && resources::game_map->is_keep(u->get_location())
00699 && !resources::whiteboard->allow_leader_to_move(*u))
00700 {
00701 gui2::show_transient_message(gui_->video(), "",
00702 _("You cannot move your leader away from the keep with some planned recruits or recalls left."));
00703
00704 if(next_unit)
00705 *next_unit = steps.front();
00706 return false;
00707 }
00708 }
00709
00710 size_t moves = 0;
00711 try {
00712 moves = ::move_unit(NULL, steps, &recorder, resources::undo_stack, true, next_unit, false, check_shroud, false, sighted_result);
00713 } catch(end_turn_exception&) {
00714 cursor::set(cursor::NORMAL);
00715 gui().invalidate_game_status();
00716 throw;
00717 }
00718
00719 cursor::set(cursor::NORMAL);
00720 gui().invalidate_game_status();
00721
00722 if(moves == 0)
00723 return false;
00724
00725 resources::redo_stack->clear();
00726
00727 assert(moves <= steps.size());
00728 const map_location& dst = steps[moves-1];
00729 const unit_map::const_iterator u = units_.find(dst);
00730
00731
00732 if(u != units_.end()) {
00733 if(dst != steps.back()) {
00734
00735 if (u->movement_left() > 0) {
00736
00737 select_hex(dst, false);
00738
00739 show_partial_move_ = true;
00740 gui().unhighlight_reach();
00741 }
00742 }
00743 }
00744
00745 return moves == steps.size();
00746 }
00747
00748 void mouse_handler::save_whiteboard_attack(const map_location& attacker_loc, const map_location& defender_loc, int weapon_choice)
00749 {
00750
00751 {
00752
00753
00754
00755
00756
00757 gui().draw();
00758 gui().unhighlight_reach();
00759 gui().clear_attack_indicator();
00760
00761
00762 gui().set_route(NULL);
00763
00764
00765 selected_hex_ = map_location();
00766 gui().select_hex(map_location());
00767 show_partial_move_ = false;
00768
00769
00770 current_paths_ = pathfind::paths();
00771 current_route_.steps.clear();
00772 }
00773
00774
00775 resources::whiteboard->save_temp_attack(attacker_loc, defender_loc, weapon_choice);
00776
00777 }
00778
00779 int mouse_handler::fill_weapon_choices(std::vector<battle_context>& bc_vector, unit_map::iterator attacker, unit_map::iterator defender)
00780 {
00781 int best = 0;
00782 for (unsigned int i = 0; i < attacker->attacks().size(); i++) {
00783
00784 if (attacker->attacks()[i].attack_weight() > 0) {
00785 battle_context bc(*resources::units, attacker->get_location(), defender->get_location(), i);
00786 bc_vector.push_back(bc);
00787 if (bc.better_attack(bc_vector[best], 0.5)) {
00788
00789 best = bc_vector.size() - 1;
00790 }
00791 }
00792 }
00793 return best;
00794 }
00795
00796 int mouse_handler::show_attack_dialog(const map_location& attacker_loc, const map_location& defender_loc)
00797 {
00798
00799 unit_map::iterator attacker = units_.find(attacker_loc);
00800 unit_map::iterator defender = units_.find(defender_loc);
00801 if(attacker == units_.end() || defender == units_.end()) {
00802 ERR_NG << "One fighter is missing, can't attack";
00803 return -1;
00804 }
00805
00806 std::vector<battle_context> bc_vector;
00807 const int best = fill_weapon_choices(bc_vector, attacker, defender);
00808
00809 if(gui2::new_widgets) {
00810 gui2::tunit_attack dlg(
00811 attacker
00812 , defender
00813 , bc_vector
00814 , best);
00815
00816 dlg.show(resources::screen->video());
00817
00818 if(dlg.get_retval() == gui2::twindow::OK) {
00819 return dlg.get_selected_weapon();
00820 } else {
00821 return -1;
00822 }
00823 }
00824
00825 if (bc_vector.empty())
00826 {
00827 dialogs::units_list_preview_pane attacker_preview(&*attacker, dialogs::unit_preview_pane::SHOW_BASIC, true);
00828 dialogs::units_list_preview_pane defender_preview(&*defender, dialogs::unit_preview_pane::SHOW_BASIC, false);
00829 std::vector<gui::preview_pane*> preview_panes;
00830 preview_panes.push_back(&attacker_preview);
00831 preview_panes.push_back(&defender_preview);
00832
00833 gui::show_dialog(gui(), NULL, _("Attack Enemy"),
00834 _("No usable weapon"), gui::CANCEL_ONLY, NULL,
00835 &preview_panes, "", NULL, -1, NULL, -1, -1, NULL, NULL);
00836 return -1;
00837 }
00838
00839
00840 std::vector<std::string> items;
00841
00842 for (unsigned int i = 0; i < bc_vector.size(); i++) {
00843 const battle_context_unit_stats& att = bc_vector[i].get_attacker_stats();
00844 const battle_context_unit_stats& def = bc_vector[i].get_defender_stats();
00845 config tmp_config;
00846 attack_type no_weapon(tmp_config);
00847 const attack_type& attw = attack_type(*att.weapon);
00848 const attack_type& defw = attack_type(def.weapon ? *def.weapon : no_weapon);
00849
00850 attw.set_specials_context(attacker->get_location(), defender->get_location(), *attacker, true);
00851 defw.set_specials_context(attacker->get_location(), defender->get_location(), *attacker, false);
00852
00853
00854
00855 std::string att_weapon_special = attw.weapon_specials();
00856 if (att_weapon_special.empty())
00857 att_weapon_special += " ";
00858 std::string def_weapon_special = defw.weapon_specials();
00859 if (def_weapon_special.empty())
00860 def_weapon_special += " ";
00861
00862 std::stringstream atts;
00863 if (static_cast<int>(i) == best) {
00864 atts << DEFAULT_ITEM;
00865 }
00866
00867 std::string range = attw.range().empty() ? defw.range() : attw.range();
00868 if (!range.empty()) {
00869 range = string_table["range_" + range];
00870 }
00871
00872
00873 std::string attw_name = attw.name();
00874 if(attw_name.empty())
00875 attw_name = " ";
00876 std::string defw_name = defw.name();
00877 if(defw_name.empty())
00878 defw_name = " ";
00879
00880
00881 SDL_Color att_cth_color =
00882 int_to_color( game_config::red_to_green(att.chance_to_hit) );
00883 SDL_Color def_cth_color =
00884 int_to_color( game_config::red_to_green(def.chance_to_hit) );
00885
00886 atts << IMAGE_PREFIX << attw.icon() << COLUMN_SEPARATOR
00887 << font::BOLD_TEXT << attw_name << "\n"
00888 << att.damage << font::weapon_numbers_sep << att.num_blows
00889 << " " << att_weapon_special << "\n"
00890 << font::color2markup(att_cth_color) << att.chance_to_hit << "%"
00891 << COLUMN_SEPARATOR << font::weapon_details << utils::unicode_em_dash + " " << range << " " + utils::unicode_em_dash << COLUMN_SEPARATOR
00892 << font::BOLD_TEXT << defw_name << "\n"
00893 << def.damage << font::weapon_numbers_sep << def.num_blows
00894 << " " << def_weapon_special << "\n"
00895 << font::color2markup(def_cth_color) << def.chance_to_hit << "%"
00896 << COLUMN_SEPARATOR << IMAGE_PREFIX << defw.icon();
00897
00898 items.push_back(atts.str());
00899 }
00900
00901 attack_prediction_displayer ap_displayer(bc_vector, attacker_loc, defender_loc);
00902 std::vector<gui::dialog_button_info> buttons;
00903 buttons.push_back(gui::dialog_button_info(&ap_displayer, _("Damage Calculations")));
00904
00905 int res = 0;
00906 {
00907 dialogs::units_list_preview_pane attacker_preview(&*attacker, dialogs::unit_preview_pane::SHOW_BASIC, true);
00908 dialogs::units_list_preview_pane defender_preview(&*defender, dialogs::unit_preview_pane::SHOW_BASIC, false);
00909 std::vector<gui::preview_pane*> preview_panes;
00910 preview_panes.push_back(&attacker_preview);
00911 preview_panes.push_back(&defender_preview);
00912
00913 res = gui::show_dialog(gui(),NULL,_("Attack Enemy"),
00914 _("Choose weapon:")+std::string("\n"),
00915 gui::OK_CANCEL,&items,&preview_panes,"",NULL,-1,NULL,-1,-1,
00916 NULL,&buttons);
00917 }
00918 cursor::set(cursor::NORMAL);
00919
00920 return res;
00921 }
00922
00923 void mouse_handler::attack_enemy(const map_location& attacker_loc, const map_location& defender_loc, int choice)
00924 {
00925 try {
00926 attack_enemy_(attacker_loc, defender_loc, choice);
00927 } catch(std::bad_alloc) {
00928 lg::wml_error << "Memory exhausted a unit has either a lot hitpoints or a negative amount.\n";
00929 }
00930 }
00931
00932 void mouse_handler::attack_enemy_(const map_location& att_loc
00933 , const map_location& def_loc
00934 , int choice)
00935 {
00936
00937
00938
00939 const map_location attacker_loc = att_loc;
00940 const map_location defender_loc = def_loc;
00941
00942
00943 apply_shroud_changes(*resources::undo_stack, side_num_);
00944 resources::undo_stack->clear();
00945 resources::redo_stack->clear();
00946
00947 unit_map::iterator attacker = find_unit(attacker_loc);
00948 if(attacker == units_.end()
00949 || attacker->side() != side_num_
00950 || attacker->incapacitated())
00951 return;
00952
00953 unit_map::iterator defender = find_unit(defender_loc);
00954 if(defender == units_.end()
00955 || current_team().is_enemy(defender->side()) == false
00956 || defender->incapacitated())
00957 return;
00958
00959 std::vector<battle_context> bc_vector;
00960 fill_weapon_choices(bc_vector, attacker, defender);
00961
00962 if(size_t(choice) >= bc_vector.size()) {
00963 return;
00964 }
00965
00966 commands_disabled++;
00967 const battle_context_unit_stats &att = bc_vector[choice].get_attacker_stats();
00968 const battle_context_unit_stats &def = bc_vector[choice].get_defender_stats();
00969
00970 attacker->set_goto(map_location());
00971
00972 current_paths_ = pathfind::paths();
00973
00974 gui().display_unit_hex(attacker_loc);
00975
00976 gui().select_hex(map_location());
00977 gui().highlight_hex(map_location());
00978 gui().clear_attack_indicator();
00979 gui().unhighlight_reach();
00980 gui().draw();
00981
00982
00983 recorder.add_attack(attacker_loc, defender_loc, att.attack_num, def.attack_num,
00984 attacker->type_id(), defender->type_id(), att.level,
00985 def.level, resources::tod_manager->turn(), resources::tod_manager->get_time_of_day());
00986 rand_rng::invalidate_seed();
00987 if (rand_rng::has_valid_seed()) {
00988 perform_attack(attacker_loc, defender_loc, att.attack_num, def.attack_num, rand_rng::get_last_seed());
00989 } else {
00990 rand_rng::set_new_seed_callback(boost::bind(&mouse_handler::perform_attack,
00991 this, attacker_loc, defender_loc, att.attack_num, def.attack_num, _1));
00992 }
00993 }
00994
00995 void mouse_handler::perform_attack(
00996 map_location attacker_loc, map_location defender_loc,
00997 int attacker_weapon, int defender_weapon, int seed)
00998 {
00999
01000
01001
01002 rand_rng::clear_new_seed_callback();
01003 LOG_NG << "Performing attack with seed " << seed << "\n";
01004 recorder.add_seed("attack", seed);
01005
01006 current_team().set_action_bonus_count(1 + current_team().action_bonus_count());
01007
01008 try {
01009 events::command_disabler disabler;
01010 commands_disabled--;
01011 attack_unit(attacker_loc, defender_loc, attacker_weapon, defender_weapon);
01012 } catch(end_level_exception&) {
01013
01014
01015 dialogs::advance_unit(attacker_loc);
01016 unit_map::const_iterator defu = units_.find(defender_loc);
01017 if (defu != units_.end()) {
01018 bool defender_human = teams_[defu->side() - 1].is_human();
01019 dialogs::advance_unit(defender_loc, !defender_human);
01020 }
01021 throw;
01022 }
01023
01024 dialogs::advance_unit(attacker_loc);
01025 unit_map::const_iterator defu = units_.find(defender_loc);
01026 if (defu != units_.end()) {
01027 bool defender_human = teams_[defu->side() - 1].is_human();
01028 dialogs::advance_unit(defender_loc, !defender_human);
01029 }
01030
01031 resources::controller->check_victory();
01032 gui().draw();
01033 }
01034
01035 std::set<map_location> mouse_handler::get_adj_enemies(const map_location& loc, int side) const
01036 {
01037 std::set<map_location> res;
01038
01039 const team& uteam = teams_[side-1];
01040
01041 map_location adj[6];
01042 get_adjacent_tiles(loc, adj);
01043 foreach (const map_location &aloc, adj) {
01044 unit_map::const_iterator i = find_unit(aloc);
01045 if (i != units_.end() && uteam.is_enemy(i->side()))
01046 res.insert(aloc);
01047 }
01048 return res;
01049 }
01050
01051
01052
01053
01054
01055
01056
01057 void mouse_handler::show_attack_options(const unit_map::const_iterator &u)
01058 {
01059
01060 if (u->attacks_left() == 0)
01061 return;
01062
01063
01064 const team & cur_team = current_team();
01065 const team & u_team = teams_[u->side()-1];
01066
01067
01068 map_location adj[6];
01069 get_adjacent_tiles(u->get_location(), adj);
01070 foreach (const map_location &loc, adj)
01071 {
01072
01073
01074 if (!map_.on_board(loc)) continue;
01075 unit_map::const_iterator i = units_.find(loc);
01076 if ( i == units_.end() || !i->is_visible_to_team(cur_team) )
01077 continue;
01078 const unit &target = *i;
01079
01080 if ( u_team.is_enemy(target.side()) && !target.incapacitated() )
01081 current_paths_.destinations.insert(loc);
01082 }
01083 }
01084
01085 bool mouse_handler::unit_in_cycle(unit_map::const_iterator it)
01086 {
01087 if (it == units_.end())
01088 return false;
01089
01090 if (it->side() != side_num_ || it->user_end_turn()
01091 || gui().fogged(it->get_location()) || !unit_can_move(*it))
01092 return false;
01093
01094 if (current_team().is_enemy(int(gui().viewing_team()+1)) &&
01095 it->invisible(it->get_location()))
01096 return false;
01097
01098 if (it->get_hidden())
01099 return false;
01100
01101 return true;
01102
01103 }
01104
01105 void mouse_handler::cycle_units(const bool browse, const bool reverse)
01106 {
01107 if (units_.begin() == units_.end()) {
01108 return;
01109 }
01110
01111 unit_map::const_iterator it = find_unit(next_unit_);
01112 if (it == units_.end())
01113 it = units_.begin();
01114 const unit_map::const_iterator itx = it;
01115
01116 do {
01117 if (reverse) {
01118 if (it == units_.begin())
01119 it = units_.end();
01120 --it;
01121 } else {
01122 if (it == units_.end())
01123 it = units_.begin();
01124 else
01125 ++it;
01126 }
01127 } while (it != itx && !unit_in_cycle(it));
01128
01129 if (unit_in_cycle(it)) {
01130 gui().scroll_to_tile(it->get_location(), game_display::WARP);
01131 select_hex(it->get_location(), browse);
01132
01133 }
01134 }
01135
01136 void mouse_handler::set_current_paths(pathfind::paths new_paths) {
01137 gui().unhighlight_reach();
01138 current_paths_ = new_paths;
01139 current_route_.steps.clear();
01140 gui().set_route(NULL);
01141 resources::whiteboard->erase_temp_move();
01142 }
01143
01144 mouse_handler *mouse_handler::singleton_ = NULL;
01145 }