00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "mouse_events.hpp"
00017
00018 #include "attack_prediction.hpp"
00019 #include "cursor.hpp"
00020 #include "dialogs.hpp"
00021 #include "game_events.hpp"
00022 #include "gettext.hpp"
00023 #include "log.hpp"
00024 #include "marked-up_text.hpp"
00025 #include "menu_events.hpp"
00026 #include "preferences_display.hpp"
00027 #include "sound.hpp"
00028 #include "replay.hpp"
00029 #include "show_dialog.hpp"
00030 #include "unit_abilities.hpp"
00031 #include "wml_separators.hpp"
00032 #include "unit_display.hpp"
00033 #include "sdl_utils.hpp"
00034
00035 #include <cassert>
00036 #include <cstdlib>
00037
00038 namespace events{
00039
00040 int commands_disabled = 0;
00041
00042 command_disabler::command_disabler()
00043 {
00044 ++commands_disabled;
00045 }
00046
00047 command_disabler::~command_disabler()
00048 {
00049 --commands_disabled;
00050 }
00051
00052 static bool command_active()
00053 {
00054 #ifdef __APPLE__
00055 return (SDL_GetModState()&KMOD_META) != 0;
00056 #else
00057 return false;
00058 #endif
00059 }
00060
00061
00062 static void format_prob(char str_buf[10], const float prob)
00063 {
00064
00065 if(prob > 0.9995) {
00066 snprintf(str_buf, 10, "100 %%");
00067 } else if(prob >= 0.1) {
00068 snprintf(str_buf, 10, "%4.1f %%",
00069 static_cast<float>(100.0 * (prob + 0.0005)));
00070 } else {
00071 snprintf(str_buf, 10, " %3.1f %%",
00072 static_cast<float>(100.0 * (prob + 0.0005)));
00073 }
00074
00075 str_buf[9] = '\0';
00076 }
00077
00078 namespace{
00079
00080 const double drag_threshold = 14.0;
00081
00082
00083 class battle_prediction_pane : public gui::preview_pane
00084 {
00085 public:
00086
00087
00088 battle_prediction_pane(game_display &disp, const battle_context& bc, const gamemap& map,
00089 const std::vector<team>& teams, const unit_map& units,
00090 const gamestatus& status,
00091 const gamemap::location& attacker_loc, const gamemap::location& defender_loc);
00092
00093
00094 void draw_contents();
00095
00096
00097 bool left_side() const { return 1; }
00098
00099
00100 void set_selection(int) {}
00101
00102 private:
00103 game_display &disp_;
00104 const battle_context& bc_;
00105 const gamemap& map_;
00106 const std::vector<team>& teams_;
00107 const unit_map& units_;
00108 const gamestatus& status_;
00109 const gamemap::location& attacker_loc_;
00110 const gamemap::location& defender_loc_;
00111 const unit& attacker_;
00112 const unit& defender_;
00113
00114
00115 static const int inter_line_gap_;
00116 static const int inter_column_gap_;
00117 static const int inter_units_gap_;
00118 static const int max_hp_distrib_rows_;
00119
00120
00121 std::string attacker_label_, defender_label_;
00122 int attacker_label_width_, defender_label_width_;
00123
00124 std::vector<std::string> attacker_left_strings_, attacker_right_strings_;
00125 std::vector<std::string> defender_left_strings_, defender_right_strings_;
00126 int attacker_strings_width_, attacker_left_strings_width_, attacker_right_strings_width_;
00127 int defender_strings_width_, defender_left_strings_width_, defender_right_strings_width_;
00128 int units_strings_height_;
00129
00130 std::string hp_distrib_string_;
00131 surface attacker_hp_distrib_, defender_hp_distrib_;
00132 int hp_distrib_string_width_;
00133 int attacker_hp_distrib_width_, defender_hp_distrib_width_;
00134 int attacker_hp_distrib_height_, defender_hp_distrib_height_, hp_distribs_height_;
00135
00136 int attacker_width_, defender_width_, units_width_;
00137 int dialog_width_, dialog_height_;
00138
00139
00140
00141 void get_unit_strings(const battle_context::unit_stats& stats,
00142 const unit& u, const gamemap::location& u_loc, float u_unscathed,
00143 const unit& opp, const gamemap::location& opp_loc, const attack_type *opp_weapon,
00144 std::vector<std::string>& left_strings, std::vector<std::string>& right_strings,
00145 int& left_strings_width, int& right_strings_width, int& strings_width);
00146
00147
00148 int get_strings_max_length(const std::vector<std::string>& strings);
00149
00150
00151
00152
00153
00154 void get_hp_prob_vector(const std::vector<double>& hp_dist,
00155 std::vector<std::pair<int, double> >& hp_prob_vector);
00156
00157
00158
00159 void draw_unit(int x_off, int damage_line_skip, int left_strings_width,
00160 const std::vector<std::string>& left_strings,
00161 const std::vector<std::string>& right_strings,
00162 const std::string& label, int label_width,
00163 surface& hp_distrib, int hp_distrib_width);
00164
00165
00166
00167
00168
00169
00170 void get_hp_distrib_surface(const std::vector<std::pair<int, double> >& hp_prob_vector,
00171 const battle_context::unit_stats& stats,
00172 const battle_context::unit_stats& opp_stats,
00173 surface& surf, int& width, int& height);
00174
00175
00176
00177
00178
00179
00180 Uint32 blend_rgb(const surface& surf, unsigned char r, unsigned char g, unsigned char b, unsigned char drop);
00181 };
00182
00183 const int battle_prediction_pane::inter_line_gap_ = 3;
00184 const int battle_prediction_pane::inter_column_gap_ = 30;
00185 const int battle_prediction_pane::inter_units_gap_ = 30;
00186 const int battle_prediction_pane::max_hp_distrib_rows_ = 10;
00187
00188 battle_prediction_pane::battle_prediction_pane(game_display &disp, const battle_context& bc, const gamemap& map,
00189 const std::vector<team>& teams, const unit_map& units,
00190 const gamestatus& status,
00191 const gamemap::location& attacker_loc, const gamemap::location& defender_loc)
00192 : gui::preview_pane(disp.video()), disp_(disp), bc_(bc), map_(map), teams_(teams), units_(units), status_(status),
00193 attacker_loc_(attacker_loc), defender_loc_(defender_loc),
00194 attacker_(units.find(attacker_loc)->second), defender_(units.find(defender_loc)->second)
00195 {
00196
00197 combatant attacker_combatant(bc.get_attacker_stats());
00198 combatant defender_combatant(bc.get_defender_stats());
00199 attacker_combatant.fight(defender_combatant);
00200
00201 const battle_context::unit_stats& attacker_stats = bc.get_attacker_stats();
00202 const battle_context::unit_stats& defender_stats = bc.get_defender_stats();
00203
00204
00205 std::vector<std::pair<int, double> > hp_prob_vector;
00206 get_hp_prob_vector(attacker_combatant.hp_dist, hp_prob_vector);
00207 get_hp_distrib_surface(hp_prob_vector, attacker_stats, defender_stats, attacker_hp_distrib_,
00208 attacker_hp_distrib_width_, attacker_hp_distrib_height_);
00209 get_hp_prob_vector(defender_combatant.hp_dist, hp_prob_vector);
00210 get_hp_distrib_surface(hp_prob_vector, defender_stats, attacker_stats, defender_hp_distrib_,
00211 defender_hp_distrib_width_, defender_hp_distrib_height_);
00212 hp_distribs_height_ = maximum<int>(attacker_hp_distrib_height_, defender_hp_distrib_height_);
00213
00214
00215 std::stringstream str;
00216
00217 attacker_label_ = _("Attacker");
00218 defender_label_ = _("Defender");
00219 attacker_label_width_ = font::line_width(attacker_label_, font::SIZE_PLUS, TTF_STYLE_BOLD);
00220 defender_label_width_ = font::line_width(defender_label_, font::SIZE_PLUS, TTF_STYLE_BOLD);
00221
00222
00223 get_unit_strings(attacker_stats, attacker_, attacker_loc_, attacker_combatant.untouched,
00224 defender_, defender_loc_, defender_stats.weapon,
00225 attacker_left_strings_, attacker_right_strings_,
00226 attacker_left_strings_width_, attacker_right_strings_width_, attacker_strings_width_);
00227
00228 get_unit_strings(defender_stats, defender_, defender_loc_, defender_combatant.untouched,
00229 attacker_, attacker_loc_, attacker_stats.weapon,
00230 defender_left_strings_, defender_right_strings_,
00231 defender_left_strings_width_, defender_right_strings_width_, defender_strings_width_);
00232
00233 units_strings_height_ = maximum<int>(attacker_left_strings_.size(), defender_left_strings_.size())
00234 * (font::SIZE_NORMAL + inter_line_gap_) + 14;
00235
00236 hp_distrib_string_ = _("Expected Battle Result (HP)");
00237 hp_distrib_string_width_ = font::line_width(hp_distrib_string_, font::SIZE_SMALL);
00238
00239 attacker_width_ = maximum<int>(attacker_label_width_, attacker_strings_width_);
00240 attacker_width_ = maximum<int>(attacker_width_, hp_distrib_string_width_);
00241 attacker_width_ = maximum<int>(attacker_width_, attacker_hp_distrib_width_);
00242 defender_width_ = maximum<int>(defender_label_width_, defender_strings_width_);
00243 defender_width_ = maximum<int>(defender_width_, hp_distrib_string_width_);
00244 defender_width_ = maximum<int>(defender_width_, defender_hp_distrib_width_);
00245 units_width_ = maximum<int>(attacker_width_, defender_width_);
00246
00247 dialog_width_ = 2 * units_width_ + inter_units_gap_;
00248 dialog_height_ = 15 + 24 + units_strings_height_ + 14 + 19 + hp_distribs_height_ + 18;
00249
00250
00251 set_measurements(dialog_width_, dialog_height_);
00252 }
00253
00254 void battle_prediction_pane::get_unit_strings(const battle_context::unit_stats& stats,
00255 const unit& u, const gamemap::location& u_loc, float u_unscathed,
00256 const unit& opp, const gamemap::location& opp_loc, const attack_type *opp_weapon,
00257 std::vector<std::string>& left_strings, std::vector<std::string>& right_strings,
00258 int& left_strings_width, int& right_strings_width, int& strings_width)
00259 {
00260 std::stringstream str;
00261 char str_buf[10];
00262
00263
00264 if(stats.weapon != NULL) {
00265
00266
00267 const attack_type *weapon = stats.weapon;
00268 weapon->set_specials_context(u_loc, opp_loc, &units_, &map_, &status_, &teams_, stats.is_attacker, opp_weapon);
00269
00270
00271 unit_ability_list dmg_specials = weapon->get_specials("damage");
00272 unit_abilities::effect dmg_effect(dmg_specials, weapon->damage(), stats.backstab_pos);
00273
00274
00275 const unit_abilities::individual_effect *set_dmg_effect = NULL;
00276 unit_abilities::effect_list::const_iterator i;
00277 for(i = dmg_effect.begin(); i != dmg_effect.end(); ++i) {
00278 if(i->type == unit_abilities::SET) {
00279 set_dmg_effect = &*i;
00280 break;
00281 }
00282 }
00283
00284
00285 if(set_dmg_effect == NULL) {
00286 left_strings.push_back(weapon->name());
00287 str.str("");
00288 str << weapon->damage();
00289 right_strings.push_back(str.str());
00290 } else {
00291 left_strings.push_back((*set_dmg_effect->ability)["name"]);
00292 str.str("");
00293 str << set_dmg_effect->value;
00294 right_strings.push_back(str.str());
00295 }
00296
00297
00298 for(i = dmg_effect.begin(); i != dmg_effect.end(); ++i) {
00299 if(i->type == unit_abilities::ADD) {
00300 left_strings.push_back((*i->ability)["name"]);
00301 str.str("");
00302 if(i->value >= 0) str << "+" << i->value;
00303 else str << i->value;
00304 right_strings.push_back(str.str());
00305 }
00306 }
00307
00308
00309 for(i = dmg_effect.begin(); i != dmg_effect.end(); ++i) {
00310 if(i->type == unit_abilities::MUL) {
00311 left_strings.push_back((*i->ability)["name"]);
00312 str.str("");
00313 str << "* " << (i->value / 100);
00314 if(i->value % 100) {
00315 str << "." << ((i->value % 100) / 10);
00316 if(i->value % 10) str << (i->value % 10);
00317 }
00318 right_strings.push_back(str.str());
00319 }
00320 }
00321
00322
00323 int resistance_modifier = opp.damage_from(*weapon, !stats.is_attacker, opp_loc);
00324 if(resistance_modifier != 100) {
00325 str.str("");
00326 if(stats.is_attacker) str << _("Defender");
00327 else str << _("Attacker");
00328 if(resistance_modifier < 100) str << _(" resistance vs ");
00329 else str << _(" vulnerability vs ");
00330 str << gettext(weapon->type().c_str());
00331 left_strings.push_back(str.str());
00332 str.str("");
00333 str << "* " << (resistance_modifier / 100) << "." << ((resistance_modifier % 100) / 10);
00334 right_strings.push_back(str.str());
00335 }
00336
00337
00338 if(stats.is_slowed) {
00339 left_strings.push_back(_("Slowed"));
00340 right_strings.push_back("* 0.5");
00341 }
00342
00343
00344 int tod_modifier = combat_modifier(status_, units_, u_loc, u.alignment(), u.is_fearless(), map_);
00345 if(tod_modifier != 0) {
00346 left_strings.push_back(_("Time of day"));
00347 str.str("");
00348 str << (tod_modifier > 0 ? "+" : "") << tod_modifier << "%";
00349 right_strings.push_back(str.str());
00350 }
00351
00352
00353 int leadership_bonus = 0;
00354 under_leadership(units_, u_loc, &leadership_bonus);
00355 if(leadership_bonus != 0) {
00356 left_strings.push_back(_("Leadership"));
00357 str.str("");
00358 str << "+" << leadership_bonus << "%";
00359 right_strings.push_back(str.str());
00360 }
00361
00362
00363 left_strings.push_back(_("Total damage"));
00364 str.str("");
00365 str << stats.damage << "-" << stats.num_blows << " (" << stats.chance_to_hit << "%)";
00366 right_strings.push_back(str.str());
00367
00368
00369 } else {
00370 left_strings.push_back(_("No usable weapon"));
00371 right_strings.push_back("");
00372 }
00373
00374
00375 left_strings.push_back(_("Chance of being unscathed"));
00376 format_prob(str_buf, u_unscathed);
00377 right_strings.push_back(str_buf);
00378
00379 #if 0 // might not be en English!
00380
00381 for(int i = 0; i < (int) left_strings.size(); i++)
00382 if(left_strings[i].size() > 0) left_strings[i][0] = toupper(left_strings[i][0]);
00383 #endif
00384
00385
00386 left_strings_width = get_strings_max_length(left_strings);
00387 right_strings_width = get_strings_max_length(right_strings);
00388 strings_width = left_strings_width + inter_column_gap_ + right_strings_width;
00389 }
00390
00391 int battle_prediction_pane::get_strings_max_length(const std::vector<std::string>& strings)
00392 {
00393 int max_len = 0;
00394
00395 for(int i = 0; i < static_cast<int>(strings.size()); i++)
00396 max_len = maximum<int>(font::line_width(strings[i], font::SIZE_NORMAL), max_len);
00397
00398 return max_len;
00399 }
00400
00401 void battle_prediction_pane::get_hp_prob_vector(const std::vector<double>& hp_dist,
00402 std::vector<std::pair<int, double> >& hp_prob_vector)
00403 {
00404 hp_prob_vector.clear();
00405
00406
00407 std::vector<std::pair<double, int> > prob_hp_vector;
00408 int i;
00409
00410 for(i = 0; i < static_cast<int>(hp_dist.size()); i++) {
00411 double prob = hp_dist[i];
00412
00413
00414 if(prob > 0.001)
00415 prob_hp_vector.push_back(std::pair<double, int>(prob, i));
00416 }
00417
00418 std::sort(prob_hp_vector.begin(), prob_hp_vector.end());
00419
00420
00421 int nb_elem = minimum<int>(max_hp_distrib_rows_, prob_hp_vector.size());
00422
00423 for(i = prob_hp_vector.size() - nb_elem;
00424 i < static_cast<int>(prob_hp_vector.size()); i++) {
00425
00426 hp_prob_vector.push_back(std::pair<int, double>
00427 (prob_hp_vector[i].second, prob_hp_vector[i].first));
00428 }
00429
00430
00431 std::sort(hp_prob_vector.begin(), hp_prob_vector.end());
00432 }
00433
00434 void battle_prediction_pane::draw_contents()
00435 {
00436
00437 int damage_line_skip = maximum<int>(attacker_left_strings_.size(), defender_left_strings_.size()) - 2;
00438
00439 draw_unit(0, damage_line_skip,
00440 attacker_left_strings_width_, attacker_left_strings_, attacker_right_strings_,
00441 attacker_label_, attacker_label_width_, attacker_hp_distrib_, attacker_hp_distrib_width_);
00442
00443 draw_unit(units_width_ + inter_units_gap_, damage_line_skip,
00444 defender_left_strings_width_, defender_left_strings_, defender_right_strings_,
00445 defender_label_, defender_label_width_, defender_hp_distrib_, defender_hp_distrib_width_);
00446 }
00447
00448 void battle_prediction_pane::draw_unit(int x_off, int damage_line_skip, int left_strings_width,
00449 const std::vector<std::string>& left_strings,
00450 const std::vector<std::string>& right_strings,
00451 const std::string& label, int label_width,
00452 surface& hp_distrib, int hp_distrib_width)
00453 {
00454 CVideo& screen = disp_.video();
00455 int i;
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465 SDL_Rect clip_rect = location();
00466 clip_rect.x += 10;
00467
00468
00469 int y_off = 15;
00470
00471
00472 font::draw_text_line(&screen, clip_rect, font::SIZE_15, font::NORMAL_COLOUR, label,
00473 clip_rect.x + x_off + (units_width_ - label_width) / 2, clip_rect.y + y_off, 0, TTF_STYLE_BOLD);
00474
00475 y_off += 24;
00476
00477
00478 for(i = 0; i < static_cast<int>(left_strings.size()) - 2; i++) {
00479 font::draw_text_line(&screen, clip_rect, font::SIZE_NORMAL, font::NORMAL_COLOUR, left_strings[i],
00480 clip_rect.x + x_off, clip_rect.y + y_off + (font::SIZE_NORMAL + inter_line_gap_) * i,
00481 0, TTF_STYLE_NORMAL);
00482
00483 font::draw_text_line(&screen, clip_rect, font::SIZE_NORMAL, font::NORMAL_COLOUR, right_strings[i],
00484 clip_rect.x + x_off + left_strings_width + inter_column_gap_,
00485 clip_rect.y + y_off + (font::SIZE_NORMAL + inter_line_gap_) * i, 0, TTF_STYLE_NORMAL);
00486 }
00487
00488
00489 y_off += damage_line_skip * (font::SIZE_NORMAL + inter_line_gap_) + 14;
00490
00491
00492 for(i = 0; i < 2; i++) {
00493 const std::string& left_string = left_strings[left_strings.size() - 2 + i];
00494 const std::string& right_string = right_strings[right_strings.size() - 2 + i];
00495
00496 font::draw_text_line(&screen, clip_rect, font::SIZE_NORMAL, font::NORMAL_COLOUR, left_string,
00497 clip_rect.x + x_off, clip_rect.y + y_off + (font::SIZE_NORMAL + inter_line_gap_) * i,
00498 0, TTF_STYLE_NORMAL);
00499
00500 font::draw_text_line(&screen, clip_rect, font::SIZE_NORMAL, font::NORMAL_COLOUR, right_string,
00501 clip_rect.x + x_off + left_strings_width + inter_column_gap_,
00502 clip_rect.y + y_off + (font::SIZE_NORMAL + inter_line_gap_) * i, 0, TTF_STYLE_NORMAL);
00503 }
00504
00505 y_off += 2 * (font::SIZE_NORMAL + inter_line_gap_) + 14;
00506
00507
00508 font::draw_text(&screen, clip_rect, font::SIZE_SMALL, font::NORMAL_COLOUR, hp_distrib_string_,
00509 clip_rect.x + x_off + (units_width_ - hp_distrib_string_width_) / 2, clip_rect.y + y_off);
00510
00511 y_off += 19;
00512
00513
00514 video().blit_surface(clip_rect.x + x_off + (units_width_ - hp_distrib_width) / 2, clip_rect.y + y_off, hp_distrib);
00515 }
00516
00517 void battle_prediction_pane::get_hp_distrib_surface(const std::vector<std::pair<int, double> >& hp_prob_vector,
00518 const battle_context::unit_stats& stats,
00519 const battle_context::unit_stats& opp_stats,
00520 surface& surf, int& width, int& height)
00521 {
00522
00523 int fs = font::SIZE_SMALL;
00524
00525
00526 int hp_sep = 24 + 6;
00527
00528
00529 int bar_space = 150;
00530
00531
00532 int percent_sep = 43 + 6;
00533
00534
00535 width = 30 + 2 + bar_space + 2 + percent_sep;
00536 height = 5 + (fs + 2) * hp_prob_vector.size();
00537
00538
00539 surf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
00540 image::pixel_format->BitsPerPixel,
00541 image::pixel_format->Rmask,
00542 image::pixel_format->Gmask,
00543 image::pixel_format->Bmask,
00544 image::pixel_format->Amask);
00545
00546 SDL_Rect clip_rect = {0, 0, width, height};
00547 Uint32 grey_color = SDL_MapRGB(surf->format, 0xb7, 0xc1, 0xc1);
00548 Uint32 transparent_color = SDL_MapRGB(surf->format, 1, 1, 1);
00549
00550
00551 SDL_SetColorKey(surf, SDL_SRCCOLORKEY, transparent_color);
00552 SDL_FillRect(surf, &clip_rect, transparent_color);
00553
00554
00555 SDL_Rect top_border_rect = {0, 0, width, 2};
00556 SDL_FillRect(surf, &top_border_rect, grey_color);
00557
00558 SDL_Rect bottom_border_rect = {0, height - 2, width, 2};
00559 SDL_FillRect(surf, &bottom_border_rect, grey_color);
00560
00561 SDL_Rect left_border_rect = {0, 0, 2, height};
00562 SDL_FillRect(surf, &left_border_rect, grey_color);
00563
00564 SDL_Rect right_border_rect = {width - 2, 0, 2, height};
00565 SDL_FillRect(surf, &right_border_rect, grey_color);
00566
00567 SDL_Rect hp_sep_rect = {hp_sep, 0, 2, height};
00568 SDL_FillRect(surf, &hp_sep_rect, grey_color);
00569
00570 SDL_Rect percent_sep_rect = {width - percent_sep - 2, 0, 2, height};
00571 SDL_FillRect(surf, &percent_sep_rect, grey_color);
00572
00573
00574 for(int i = 0; i < static_cast<int>(hp_prob_vector.size()); i++) {
00575 char str_buf[10];
00576
00577
00578 int hp = hp_prob_vector[hp_prob_vector.size() - i - 1].first;
00579 double prob = hp_prob_vector[hp_prob_vector.size() - i - 1].second;
00580
00581 SDL_Color row_color;
00582
00583
00584 if(hp == 0) {
00585 SDL_Color color = {0xe5, 0, 0, 0};
00586 row_color = color;
00587 }
00588
00589
00590 else if(hp < static_cast<int>(stats.hp)) {
00591
00592 if(opp_stats.stones) {
00593 SDL_Color color = {0x9a, 0x9a, 0x9a, 0};
00594 row_color = color;
00595 } else {
00596 SDL_Color color = {0xf4, 0xc9, 0, 0};
00597 row_color = color;
00598 }
00599 }
00600
00601
00602 else {
00603 SDL_Color color = {0x08, 0xca, 0, 0};
00604 row_color = color;
00605 }
00606
00607
00608 snprintf(str_buf, 10, "%d", hp);
00609 str_buf[9] = '\0';
00610 int hp_width = font::line_width(str_buf, fs);
00611
00612
00613 font::draw_text_line(surf, clip_rect, fs, font::NORMAL_COLOUR, str_buf,
00614 hp_sep - hp_width - 2, 2 + (fs + 2) * i, 0, TTF_STYLE_NORMAL);
00615
00616 int bar_len = maximum<int>(static_cast<int>((prob * (bar_space - 4)) + 0.5), 2);
00617
00618 SDL_Rect bar_rect_1 = {hp_sep + 4, 6 + (fs + 2) * i, bar_len, 8};
00619 SDL_FillRect(surf, &bar_rect_1, blend_rgb(surf, row_color.r, row_color.g, row_color.b, 100));
00620
00621 SDL_Rect bar_rect_2 = {hp_sep + 4, 7 + (fs + 2) * i, bar_len, 6};
00622 SDL_FillRect(surf, &bar_rect_2, blend_rgb(surf, row_color.r, row_color.g, row_color.b, 66));
00623
00624 SDL_Rect bar_rect_3 = {hp_sep + 4, 8 + (fs + 2) * i, bar_len, 4};
00625 SDL_FillRect(surf, &bar_rect_3, blend_rgb(surf, row_color.r, row_color.g, row_color.b, 33));
00626
00627 SDL_Rect bar_rect_4 = {hp_sep + 4, 9 + (fs + 2) * i, bar_len, 2};
00628 SDL_FillRect(surf, &bar_rect_4, blend_rgb(surf, row_color.r, row_color.g, row_color.b, 0));
00629
00630
00631 format_prob(str_buf, prob);
00632 int prob_width = font::line_width(str_buf, fs);
00633 font::draw_text_line(surf, clip_rect, fs, font::NORMAL_COLOUR, str_buf,
00634 width - prob_width - 4, 2 + (fs + 2) * i, 0, TTF_STYLE_NORMAL);
00635 }
00636 }
00637
00638 Uint32 battle_prediction_pane::blend_rgb(const surface& surf, unsigned char r, unsigned char g, unsigned char b, unsigned char drop)
00639 {
00640
00641 if(r < drop) r = 0; else r -= drop;
00642 if(g < drop) g = 0; else g -= drop;
00643 if(b < drop) b = 0; else b -= drop;
00644
00645 return SDL_MapRGB(surf->format, r, g, b);
00646 }
00647
00648
00649
00650 class attack_prediction_displayer : public gui::dialog_button_action
00651 {
00652 public:
00653 attack_prediction_displayer(game_display& disp, const std::vector<battle_context>& bc_vector, const gamemap& map,
00654 const std::vector<team>& teams, const unit_map& units,
00655 const gamestatus& status,
00656 const gamemap::location& attacker_loc, const gamemap::location& defender_loc)
00657 : disp_(disp), bc_vector_(bc_vector), map_(map), teams_(teams), units_(units), status_(status),
00658 attacker_loc_(attacker_loc), defender_loc_(defender_loc) {}
00659
00660
00661 RESULT button_pressed(int selection)
00662 {
00663
00664 const size_t index = size_t(selection);
00665
00666 if(index < bc_vector_.size()) {
00667 battle_prediction_pane battle_pane(disp_, bc_vector_[index], map_, teams_, units_, status_,
00668 attacker_loc_, defender_loc_);
00669 std::vector<gui::preview_pane*> preview_panes;
00670 preview_panes.push_back(&battle_pane);
00671
00672 gui::show_dialog(disp_, NULL, _("Damage Calculations"), "", gui::OK_ONLY, NULL, &preview_panes);
00673 }
00674
00675 return gui::CONTINUE_DIALOG;
00676 }
00677
00678 private:
00679 game_display &disp_;
00680 const std::vector<battle_context>& bc_vector_;
00681 const gamemap& map_;
00682 const std::vector<team>& teams_;
00683 const unit_map& units_;
00684 const gamestatus& status_;
00685 const gamemap::location& attacker_loc_;
00686 const gamemap::location& defender_loc_;
00687 };
00688 }
00689
00690 mouse_handler::mouse_handler(game_display* gui, std::vector<team>& teams, unit_map& units, gamemap& map,
00691 gamestatus& status, undo_list& undo_stack, undo_list& redo_stack):
00692 gui_(gui), teams_(teams), units_(units), map_(map), status_(status),
00693 undo_stack_(undo_stack), redo_stack_(redo_stack)
00694 {
00695 singleton_ = this;
00696 minimap_scrolling_ = false;
00697 dragging_ = false;
00698 dragging_started_ = false;
00699 drag_from_x_ = 0;
00700 drag_from_y_ = 0;
00701 enemy_paths_ = false;
00702 path_turns_ = 0;
00703 undo_ = false;
00704 show_menu_ = false;
00705 over_route_ = false;
00706 team_num_ = 1;
00707 attackmove_ = false;
00708 reachmap_invalid_ = false;
00709 show_partial_move_ = false;
00710 }
00711 mouse_handler::~mouse_handler()
00712 {
00713 singleton_ = NULL;
00714 }
00715
00716 void mouse_handler::set_team(const int team_number)
00717 {
00718 team_num_ = team_number;
00719 }
00720
00721 void mouse_handler::mouse_motion(const SDL_MouseMotionEvent& event, const bool browse)
00722 {
00723 mouse_motion(event.x,event.y, browse);
00724 }
00725
00726 void mouse_handler::mouse_update(const bool browse)
00727 {
00728 int x, y;
00729 SDL_GetMouseState(&x,&y);
00730 mouse_motion(x, y, browse, true);
00731 }
00732
00733 void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update)
00734 {
00735 if(attackmove_) return;
00736
00737 if(minimap_scrolling_) {
00738
00739
00740
00741 minimap_scrolling_ = ((SDL_GetMouseState(NULL,NULL) & (SDL_BUTTON(1) | SDL_BUTTON(2))) != 0);
00742 if(minimap_scrolling_) {
00743 const gamemap::location& loc = (*gui_).minimap_location_on(x,y);
00744 if(loc.valid()) {
00745 if(loc != last_hex_) {
00746 last_hex_ = loc;
00747 (*gui_).scroll_to_tile(loc,game_display::WARP,false);
00748 }
00749 } else {
00750
00751 minimap_scrolling_ = false;
00752 }
00753 }
00754 if(minimap_scrolling_) return;
00755 }
00756
00757 const gamemap::location new_hex = (*gui_).hex_clicked_on(x,y);
00758
00759
00760
00761 int mx = drag_from_x_;
00762 int my = drag_from_y_;
00763 if (dragging_ && !dragging_started_ && (SDL_GetMouseState(&mx,&my) & SDL_BUTTON_LEFT) != 0) {
00764 const double drag_distance = std::pow((double) (drag_from_x_- mx), 2) + std::pow((double) (drag_from_y_- my), 2);
00765 if (drag_distance > drag_threshold*drag_threshold) {
00766 dragging_started_ = true;
00767 cursor::set_dragging(true);
00768 }
00769 }
00770
00771 if(new_hex != last_hex_) {
00772 update = true;
00773
00774 if (last_hex_.valid()) {
00775
00776 previous_hex_ = last_hex_;
00777
00778 if (last_hex_ == selected_hex_ || find_unit(last_hex_) == units_.end()) {
00779 previous_free_hex_ = last_hex_;
00780 }
00781 }
00782 last_hex_ = new_hex;
00783 }
00784
00785 if (reachmap_invalid_) update = true;
00786
00787 if (update) {
00788 if (reachmap_invalid_) {
00789 reachmap_invalid_ = false;
00790 if (!current_paths_.routes.empty() && !show_partial_move_) {
00791 unit_map::iterator u = find_unit(selected_hex_);
00792 if(selected_hex_.valid() && u != units_.end() ) {
00793
00794 select_hex(selected_hex_, true);
00795 }
00796
00797 }
00798 }
00799
00800
00801
00802 if(new_hex.valid() == false) {
00803 current_route_.steps.clear();
00804 (*gui_).set_route(NULL);
00805 }
00806
00807 (*gui_).highlight_hex(new_hex);
00808
00809 const unit_map::iterator selected_unit = find_unit(selected_hex_);
00810 const unit_map::iterator mouseover_unit = find_unit(new_hex);
00811
00812
00813 gamemap::location attack_from = current_unit_attacks_from(new_hex);
00814
00815
00816
00817
00818
00819 if (cursor::get() != cursor::WAIT) {
00820 if(selected_unit != units_.end() && selected_unit->second.side() == team_num_
00821 && !selected_unit->second.incapacitated() && !browse) {
00822 if (attack_from.valid()) {
00823 cursor::set(dragging_started_ ? cursor::ATTACK_DRAG : cursor::ATTACK);
00824 } else if (mouseover_unit==units_.end() && current_paths_.routes.count(new_hex)) {
00825 cursor::set(dragging_started_ ? cursor::MOVE_DRAG : cursor::MOVE);
00826 } else {
00827
00828 cursor::set(cursor::NORMAL);
00829 }
00830 } else {
00831
00832 cursor::set(cursor::NORMAL);
00833 }
00834 }
00835
00836
00837 if (attack_from.valid() && !browse) {
00838 gui_->set_attack_indicator(attack_from, new_hex);
00839 } else {
00840 gui_->clear_attack_indicator();
00841 }
00842
00843 if(enemy_paths_) {
00844 enemy_paths_ = false;
00845 current_paths_ = paths();
00846 gui_->unhighlight_reach();
00847 } else if(over_route_) {
00848 over_route_ = false;
00849 current_route_.steps.clear();
00850 (*gui_).set_route(NULL);
00851 }
00852
00853
00854
00855 gamemap::location dest;
00856 unit_map::const_iterator dest_un;
00857 if (attack_from.valid()) {
00858 dest = attack_from;
00859 dest_un = find_unit(dest);
00860 } else {
00861 dest = new_hex;
00862 dest_un = mouseover_unit;
00863 }
00864
00865 if(dest == selected_hex_ || dest_un != units_.end()) {
00866 current_route_.steps.clear();
00867 (*gui_).set_route(NULL);
00868 } else if(!current_paths_.routes.empty() && map_.on_board(selected_hex_) &&
00869 map_.on_board(new_hex)) {
00870
00871 if(selected_unit != units_.end() && !selected_unit->second.incapacitated()) {
00872
00873 unit_movement_resetter move_reset(selected_unit->second,
00874 selected_unit->second.side() != team_num_);
00875 current_route_ = get_route(selected_unit, dest, viewing_team());
00876 if(!browse) {
00877 (*gui_).set_route(¤t_route_);
00878 }
00879 }
00880 }
00881
00882 unit_map::iterator un = mouseover_unit;
00883
00884 if(un != units_.end() && current_paths_.routes.empty() && !(*gui_).fogged(un->first)) {
00885 if (un->second.side() != team_num_) {
00886
00887 unit_movement_resetter move_reset(un->second);
00888
00889 const bool teleport = un->second.get_ability_bool("teleport",un->first);
00890 current_paths_ = paths(map_,units_,new_hex,teams_,
00891 false,teleport,viewing_team(),path_turns_);
00892 gui_->highlight_reach(current_paths_);
00893 enemy_paths_ = true;
00894 } else {
00895
00896 const gamemap::location go_to = un->second.get_goto();
00897 if(map_.on_board(go_to)) {
00898 paths::route route = get_route(un, go_to, current_team());
00899 gui_->set_route(&route);
00900 }
00901 over_route_ = true;
00902 }
00903 }
00904 }
00905 }
00906
00907 unit_map::iterator mouse_handler::selected_unit()
00908 {
00909 unit_map::iterator res = find_unit(selected_hex_);
00910 if(res != units_.end()) {
00911 return res;
00912 } else {
00913 return find_unit(last_hex_);
00914 }
00915 }
00916
00917 unit_map::iterator mouse_handler::find_unit(const gamemap::location& hex)
00918 {
00919 return find_visible_unit(units_,hex,map_,teams_,viewing_team());
00920 }
00921
00922 unit_map::const_iterator mouse_handler::find_unit(const gamemap::location& hex) const
00923 {
00924 return find_visible_unit(units_,hex,map_,teams_,viewing_team());
00925 }
00926
00927 gamemap::location mouse_handler::current_unit_attacks_from(const gamemap::location& loc)
00928 {
00929 const unit_map::const_iterator current = find_unit(selected_hex_);
00930 if(current == units_.end() || current->second.side() != team_num_
00931 || current->second.attacks_left()==0) {
00932 return gamemap::location();
00933 }
00934
00935 const unit_map::const_iterator enemy = find_unit(loc);
00936 if(enemy == units_.end() || current_team().is_enemy(enemy->second.side()) == false
00937 || enemy->second.incapacitated())
00938 {
00939 return gamemap::location();
00940 }
00941
00942 const gamemap::location::DIRECTION preferred = loc.get_relative_dir(previous_hex_);
00943 const gamemap::location::DIRECTION second_preferred = loc.get_relative_dir(previous_free_hex_);
00944
00945 int best_rating = 100;
00946 gamemap::location res;
00947 gamemap::location adj[6];
00948 get_adjacent_tiles(loc,adj);
00949
00950 for(size_t n = 0; n != 6; ++n) {
00951 if(map_.on_board(adj[n]) == false) {
00952 continue;
00953 }
00954
00955 if(adj[n] != selected_hex_ && find_unit(adj[n]) != units_.end()) {
00956 continue;
00957 }
00958
00959 if(current_paths_.routes.count(adj[n])) {
00960 static const size_t NDIRECTIONS = gamemap::location::NDIRECTIONS;
00961 unsigned int difference = abs(int(preferred - n));
00962 if(difference > NDIRECTIONS/2) {
00963 difference = NDIRECTIONS - difference;
00964 }
00965 unsigned int second_difference = abs(int(second_preferred - n));
00966 if(second_difference > NDIRECTIONS/2) {
00967 second_difference = NDIRECTIONS - second_difference;
00968 }
00969 const int rating = difference * 2 + (second_difference > difference);
00970 if(rating < best_rating || res.valid() == false) {
00971 best_rating = rating;
00972 res = adj[n];
00973 }
00974 }
00975 }
00976
00977 return res;
00978 }
00979
00980 paths::route mouse_handler::get_route(unit_map::const_iterator un, gamemap::location go_to, team &team)
00981 {
00982
00983 const shortest_path_calculator calc(un->second,team,units_,teams_,map_);
00984
00985 std::set<gamemap::location> allowed_teleports;
00986
00987 if(un->second.get_ability_bool("teleport",un->first)) {
00988
00989 for(std::set<gamemap::location>::const_iterator i = team.villages().begin();
00990 i != team.villages().end(); ++i) {
00991 if (viewing_team().is_enemy(un->second.side()) && viewing_team().fogged(*i))
00992 continue;
00993
00994 unit_map::const_iterator occupant = find_unit(*i);
00995 if (occupant != units_.end() && occupant != un)
00996 continue;
00997
00998 allowed_teleports.insert(*i);
00999 }
01000 }
01001
01002 paths::route route = a_star_search(un->first, go_to, 10000.0, &calc, map_.w(), map_.h(), &allowed_teleports);
01003
01004 route_turns_to_complete(un->second, route, viewing_team(), units_,teams_,map_);
01005
01006 return route;
01007 }
01008
01009 void mouse_handler::mouse_press(const SDL_MouseButtonEvent& event, const bool browse)
01010 {
01011 if(attackmove_) return;
01012 show_menu_ = false;
01013 mouse_update(browse);
01014 int scrollx = 0;
01015 int scrolly = 0;
01016
01017 if(is_left_click(event) && event.state == SDL_RELEASED) {
01018 minimap_scrolling_ = false;
01019 dragging_ = false;
01020 cursor::set_dragging(false);
01021 if (dragging_started_ && !browse && !commands_disabled) {
01022 left_click(event, browse);
01023 }
01024 dragging_started_= false;
01025 } else if(is_middle_click(event) && event.state == SDL_RELEASED) {
01026 minimap_scrolling_ = false;
01027 } else if(is_left_click(event) && event.state == SDL_PRESSED) {
01028 left_click(event, browse);
01029 if (!browse && !commands_disabled) {
01030 dragging_ = true;
01031 dragging_started_ = false;
01032 SDL_GetMouseState(&drag_from_x_, &drag_from_y_);
01033 }
01034 } else if(is_right_click(event) && event.state == SDL_PRESSED) {
01035
01036
01037 dragging_ = false;
01038 dragging_started_ = false;
01039 cursor::set_dragging(false);
01040 if (selected_hex_.valid() && find_unit(selected_hex_) != units_.end()) {
01041 select_hex(gamemap::location(), browse);
01042 } else {
01043 gui_->draw();
01044 const theme::menu* const m = gui_->get_theme().context_menu();
01045 if (m != NULL)
01046 show_menu_ = true;
01047 else
01048 LOG_STREAM(warn, display) << "no context menu found...\n";
01049 }
01050 } else if(is_middle_click(event) && event.state == SDL_PRESSED) {
01051
01052 const gamemap::location& loc = gui_->minimap_location_on(event.x,event.y);
01053 minimap_scrolling_ = false;
01054 if(loc.valid()) {
01055 minimap_scrolling_ = true;
01056 last_hex_ = loc;
01057 gui_->scroll_to_tile(loc,game_display::WARP,false);
01058 }
01059 } else if (event.button == SDL_BUTTON_WHEELUP) {
01060 scrolly = - preferences::scroll_speed();
01061 } else if (event.button == SDL_BUTTON_WHEELDOWN) {
01062 scrolly = preferences::scroll_speed();
01063 } else if (event.button == SDL_BUTTON_WHEELLEFT) {
01064 scrollx = - preferences::scroll_speed();
01065 } else if (event.button == SDL_BUTTON_WHEELRIGHT) {
01066 scrollx = preferences::scroll_speed();
01067 }
01068
01069 if (scrollx != 0 || scrolly != 0) {
01070 CKey pressed;
01071
01072 if (pressed[SDLK_LALT] || pressed[SDLK_RALT])
01073 gui_->scroll(scrolly,scrollx);
01074 else
01075 gui_->scroll(scrollx,scrolly);
01076 }
01077
01078 if (!dragging_ && dragging_started_) {
01079 dragging_started_ = false;
01080 cursor::set_dragging(false);
01081 }
01082
01083 mouse_update(browse);
01084
01085 }
01086
01087 bool mouse_handler::is_left_click(const SDL_MouseButtonEvent& event)
01088 {
01089 return event.button == SDL_BUTTON_LEFT && !command_active();
01090 }
01091
01092 bool mouse_handler::is_middle_click(const SDL_MouseButtonEvent& event)
01093 {
01094 return event.button == SDL_BUTTON_MIDDLE;
01095 }
01096
01097 bool mouse_handler::is_right_click(const SDL_MouseButtonEvent& event)
01098 {
01099 return event.button == SDL_BUTTON_RIGHT || (event.button == SDL_BUTTON_LEFT && command_active());
01100 }
01101
01102 void mouse_handler::left_click(const SDL_MouseButtonEvent& event, const bool browse)
01103 {
01104 dragging_ = false;
01105 dragging_started_ = false;
01106 cursor::set_dragging(false);
01107 undo_ = false;
01108 bool check_shroud = teams_[team_num_ - 1].auto_shroud_updates();
01109
01110
01111 const gamemap::location& loc = gui_->minimap_location_on(event.x,event.y);
01112 minimap_scrolling_ = false;
01113 if(loc.valid()) {
01114 minimap_scrolling_ = true;
01115 last_hex_ = loc;
01116 gui_->scroll_to_tile(loc,game_display::WARP,false);
01117 return;
01118 }
01119
01120
01121
01122 gamemap::location hex = last_hex_;
01123
01124 unit_map::iterator u = find_unit(selected_hex_);
01125
01126
01127
01128 if(u != units_.end() && !browse && selected_hex_ == hex && u->second.side() == team_num_) {
01129 u->second.set_goto(gamemap::location());
01130 }
01131
01132 unit_map::iterator clicked_u = find_unit(hex);
01133
01134 const gamemap::location src = selected_hex_;
01135 paths orig_paths = current_paths_;
01136 const gamemap::location& attack_from = current_unit_attacks_from(hex);
01137
01138
01139 if(!browse && !commands_disabled && attack_from.valid()) {
01140 if (attack_from == selected_hex_) {
01141 if (attack_enemy(u, clicked_u) == false) {
01142 return;
01143 }
01144 }
01145 else if (move_unit_along_current_route(false, true)) {
01146
01147
01148 u = find_unit(attack_from);
01149 unit_map::iterator enemy = find_unit(hex);
01150 if(u != units_.end() && u->second.side() == team_num_ &&
01151 enemy != units_.end() && current_team().is_enemy(enemy->second.side()) && !enemy->second.incapacitated()) {
01152
01153 std::set<gamemap::location> known_units;
01154
01155 if (teams_[team_num_-1].uses_shroud() || teams_[team_num_-1].uses_fog()){
01156 for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u) {
01157 if(teams_[team_num_-1].fogged(u->first) == false) {
01158 known_units.insert(u->first);
01159 teams_[team_num_-1].see(u->second.side()-1);
01160 }
01161 }
01162 }
01163
01164
01165 gui_->select_hex(attack_from);
01166
01167 if(!commands_disabled && attack_enemy(u,enemy) == false) {
01168 undo_ = true;
01169 selected_hex_ = src;
01170 gui_->select_hex(src);
01171 current_paths_ = orig_paths;
01172 gui_->highlight_reach(current_paths_);
01173 return;
01174 }
01175 else
01176 {
01177 if (teams_[team_num_-1].uses_shroud() || teams_[team_num_-1].uses_fog()){
01178
01179 if (clear_shroud(*gui_, map_, units_, teams_, team_num_ - 1)||!teams_[team_num_-1].auto_shroud_updates()){
01180 clear_undo_stack();
01181 gui_->invalidate_all();
01182 gui_->recalculate_minimap();
01183 gui_->draw();
01184
01185 for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u) {
01186 if(teams_[team_num_-1].fogged(u->first) == false) {
01187
01188 if (known_units.find(u->first)==known_units.end())
01189 {
01190 game_events::raise("sighted",u->first,attack_from);
01191 }
01192 }
01193 }
01194 game_events::pump();
01195 return;
01196 }
01197 }
01198 }
01199 }
01200 }
01201
01202 if(check_shroud && clear_shroud(*gui_, map_, units_, teams_, team_num_ - 1)) {
01203 clear_undo_stack();
01204 gui_->invalidate_all();
01205 gui_->draw();
01206 }
01207
01208 return;
01209 }
01210
01211
01212 else if(!commands_disabled && !browse && selected_hex_.valid() && selected_hex_ != hex &&
01213 u != units_.end() && u->second.side() == team_num_ &&
01214 clicked_u == units_.end() && !current_route_.steps.empty() &&
01215 current_route_.steps.front() == selected_hex_) {
01216
01217 gui_->unhighlight_reach();
01218 move_unit_along_current_route(check_shroud);
01219 } else {
01220
01221 select_hex(hex, browse);
01222 }
01223 }
01224
01225 void mouse_handler::select_hex(const gamemap::location& hex, const bool browse) {
01226 selected_hex_ = hex;
01227 gui_->select_hex(hex);
01228 gui_->clear_attack_indicator();
01229 gui_->set_route(NULL);
01230 show_partial_move_ = false;
01231
01232 unit_map::iterator u = find_unit(hex);
01233 if(hex.valid() && u != units_.end() ) {
01234 next_unit_ = u->first;
01235
01236
01237 unit_movement_resetter move_reset(u->second, u->second.side() != team_num_);
01238 const bool teleport = u->second.get_ability_bool("teleport",u->first);
01239 current_paths_ = paths(map_,units_,hex,teams_,
01240 false,teleport,viewing_team(),path_turns_);
01241 show_attack_options(u);
01242 gui_->highlight_reach(current_paths_);
01243
01244
01245 enemy_paths_ = false;
01246 gui_->set_route(NULL);
01247
01248
01249 if (!browse && !commands_disabled && u->second.side() == gui_->viewing_team()+1) {
01250 sound::play_UI_sound("select-unit.wav");
01251 u->second.set_selecting(*gui_, u->first);
01252
01253 game_events::fire("select", hex);
01254 }
01255
01256 } else {
01257 gui_->unhighlight_reach();
01258 current_paths_ = paths();
01259 current_route_.steps.clear();
01260 }
01261 }
01262
01263 void mouse_handler::deselect_hex() {
01264 select_hex(gamemap::location(), true);
01265 }
01266
01267 void mouse_handler::clear_undo_stack()
01268 {
01269 if(teams_[team_num_ - 1].auto_shroud_updates() == false)
01270 apply_shroud_changes(undo_stack_,gui_,map_,units_,teams_,team_num_-1);
01271 undo_stack_.clear();
01272 }
01273
01274 bool mouse_handler::move_unit_along_current_route(bool check_shroud, bool attackmove)
01275 {
01276 const std::vector<gamemap::location> steps = current_route_.steps;
01277 if(steps.empty()) {
01278 return false;
01279 }
01280
01281
01282 gui_->set_route(NULL);
01283
01284
01285 selected_hex_ = gamemap::location();
01286 gui_->select_hex(gamemap::location());
01287
01288
01289 current_paths_ = paths();
01290 current_route_.steps.clear();
01291
01292 attackmove_ = attackmove;
01293 const size_t moves = ::move_unit(gui_,map_,units_,teams_,
01294 steps,&recorder,&undo_stack_,&next_unit_,false,check_shroud);
01295 attackmove_ = false;
01296
01297 cursor::set(cursor::NORMAL);
01298
01299 gui_->invalidate_game_status();
01300
01301 if(moves == 0)
01302 return false;
01303
01304 redo_stack_.clear();
01305
01306 assert(moves <= steps.size());
01307 const gamemap::location& dst = steps[moves-1];
01308 const unit_map::const_iterator u = units_.find(dst);
01309
01310
01311 if(u != units_.end()) {
01312 if(dst != steps.back()) {
01313
01314 if (u->second.movement_left() > 0) {
01315
01316 select_hex(dst, false);
01317
01318 show_partial_move_ = true;
01319 gui_->unhighlight_reach();
01320 }
01321 }
01322 }
01323
01324 return moves == steps.size();
01325 }
01326
01327 bool mouse_handler::attack_enemy(unit_map::iterator attacker, unit_map::iterator defender)
01328 {
01329 try {
01330 return attack_enemy_(attacker, defender);
01331 } catch(std::bad_alloc) {
01332 lg::wml_error << "Memory exhausted a unit has either a lot hitpoints or a negative amount.\n";
01333 return false;
01334 }
01335
01336 }
01337
01338 bool mouse_handler::attack_enemy_(unit_map::iterator attacker, unit_map::iterator defender)
01339 {
01340
01341
01342 const gamemap::location attacker_loc = attacker->first;
01343 const gamemap::location defender_loc = defender->first;
01344
01345 std::vector<std::string> items;
01346
01347 std::vector<battle_context> bc_vector;
01348 unsigned int i, best = 0;
01349 for (i = 0; i < attacker->second.attacks().size(); i++) {
01350
01351 if (attacker->second.attacks()[i].attack_weight() > 0) {
01352 battle_context bc(map_, teams_, units_, status_, attacker->first, defender->first, i);
01353 bc_vector.push_back(bc);
01354 if (bc.better_attack(bc_vector[best], 0.5)) {
01355 best = i;
01356 }
01357 }
01358 }
01359
01360 for (i = 0; i < bc_vector.size(); i++) {
01361 const battle_context::unit_stats& att = bc_vector[i].get_attacker_stats();
01362 const battle_context::unit_stats& def = bc_vector[i].get_defender_stats();
01363 config tmp_config;
01364 attack_type no_weapon(tmp_config);
01365 const attack_type& attw = attack_type(*att.weapon);
01366 const attack_type& defw = attack_type(def.weapon ? *def.weapon : no_weapon);
01367
01368 attw.set_specials_context(attacker->first, defender->first, attacker->second, true);
01369 defw.set_specials_context(attacker->first, defender->first, attacker->second, false);
01370
01371
01372
01373 std::string special_pad = "";
01374 if (!attw.weapon_specials().empty() || !defw.weapon_specials().empty())
01375 special_pad = " ";
01376
01377 std::stringstream atts;
01378 if (i == best) {
01379 atts << DEFAULT_ITEM;
01380 }
01381
01382 std::string range = attw.range().empty() ? defw.range() : attw.range();
01383 if (!range.empty()) {
01384 range = gettext(range.c_str());
01385 }
01386 atts << IMAGE_PREFIX << attw.icon() << COLUMN_SEPARATOR
01387 << font::BOLD_TEXT << attw.name() << "\n" << att.damage << "-"
01388 << att.num_blows << " " << " (" << att.chance_to_hit << "%)\n"
01389 << attw.weapon_specials() << special_pad
01390 << COLUMN_SEPARATOR << "<245,230,193>" << "- " << range << " -" << COLUMN_SEPARATOR
01391 << font::BOLD_TEXT << defw.name() << "\n" << def.damage << "-"
01392 << def.num_blows << " " << " (" << def.chance_to_hit << "%)\n"
01393 << defw.weapon_specials() << special_pad << COLUMN_SEPARATOR
01394 << IMAGE_PREFIX << defw.icon();
01395
01396 items.push_back(atts.str());
01397 }
01398
01399
01400
01401
01402 gui_->highlight_hex(gamemap::location());
01403 gui_->draw(true,true);
01404
01405 attack_prediction_displayer ap_displayer(*gui_, bc_vector, map_, teams_, units_, status_, attacker_loc, defender_loc);
01406 std::vector<gui::dialog_button_info> buttons;
01407 buttons.push_back(gui::dialog_button_info(&ap_displayer, _("Damage Calculations")));
01408
01409 int res = 0;
01410
01411 {
01412 dialogs::units_list_preview_pane attacker_preview(*gui_,&map_,attacker->second,dialogs::unit_preview_pane::SHOW_BASIC,true);
01413 dialogs::units_list_preview_pane defender_preview(*gui_,&map_,defender->second,dialogs::unit_preview_pane::SHOW_BASIC,false);
01414 std::vector<gui::preview_pane*> preview_panes;
01415 preview_panes.push_back(&attacker_preview);
01416 preview_panes.push_back(&defender_preview);
01417
01418 res = gui::show_dialog(*gui_,NULL,_("Attack Enemy"),
01419 _("Choose weapon:")+std::string("\n"),
01420 gui::OK_CANCEL,&items,&preview_panes,"",NULL,-1,NULL,-1,-1,
01421 NULL,&buttons);
01422 }
01423
01424 cursor::set(cursor::NORMAL);
01425 if(size_t(res) < bc_vector.size()) {
01426 const battle_context::unit_stats &att = bc_vector[res].get_attacker_stats();
01427 const battle_context::unit_stats &def = bc_vector[res].get_defender_stats();
01428
01429 attacker->second.set_goto(gamemap::location());
01430 clear_undo_stack();
01431 redo_stack_.clear();
01432
01433 current_paths_ = paths();
01434 gui_->clear_attack_indicator();
01435 gui_->unhighlight_reach();
01436
01437 gui_->draw();
01438
01439 const bool defender_human = teams_[defender->second.side()-1].is_human();
01440
01441 recorder.add_attack(attacker_loc,defender_loc,att.attack_num,def.attack_num);
01442
01443
01444 current_team().set_action_bonus_count(1 + current_team().action_bonus_count());
01445
01446 try {
01447 attack(*gui_,map_,teams_,attacker_loc,defender_loc,att.attack_num,def.attack_num,units_,status_);
01448 } catch(end_level_exception&) {
01449
01450
01451 dialogs::advance_unit(map_,units_,attacker_loc,*gui_);
01452 dialogs::advance_unit(map_,units_,defender_loc,*gui_,!defender_human);
01453 throw;
01454 }
01455
01456 dialogs::advance_unit(map_,units_,attacker_loc,*gui_);
01457 dialogs::advance_unit(map_,units_,defender_loc,*gui_,!defender_human);
01458
01459 check_victory(units_, teams_, *gui_);
01460
01461 gui_->draw();
01462
01463 return true;
01464 } else {
01465 return false;
01466 }
01467 }
01468
01469 void mouse_handler::show_attack_options(unit_map::const_iterator u)
01470 {
01471 team& current_team = teams_[team_num_-1];
01472
01473 if(u == units_.end() || u->second.attacks_left() == 0)
01474 return;
01475
01476 for(unit_map::const_iterator target = units_.begin(); target != units_.end(); ++target) {
01477 if(current_team.is_enemy(target->second.side()) &&
01478 distance_between(target->first,u->first) == 1 && !target->second.incapacitated()) {
01479 current_paths_.routes[target->first] = paths::route();
01480 }
01481 }
01482 }
01483
01484 bool mouse_handler::unit_in_cycle(unit_map::const_iterator it)
01485 {
01486 if (it == units_.end())
01487 return false;
01488
01489 if(it->second.side() != team_num_ || it->second.user_end_turn()
01490 || gui_->fogged(it->first) || !unit_can_move(it->first,units_,map_,teams_))
01491 return false;
01492
01493 if (current_team().is_enemy(int(gui_->viewing_team()+1)) &&
01494 it->second.invisible(it->first,units_,teams_))
01495 return false;
01496
01497 return true;
01498
01499 }
01500
01501 void mouse_handler::cycle_units(const bool browse, const bool reverse)
01502 {
01503 if (units_.begin() == units_.end()) {
01504 return;
01505 }
01506
01507 unit_map::const_iterator it = find_unit(next_unit_);
01508 if (it == units_.end())
01509 it = units_.begin();
01510 const unit_map::const_iterator itx = it;
01511
01512 do {
01513 if (reverse) {
01514 if (it == units_.begin())
01515 it = units_.end();
01516 --it;
01517 } else {
01518 if (it == units_.end())
01519 it = units_.begin();
01520 else
01521 ++it;
01522 }
01523 } while (it != itx && !unit_in_cycle(it));
01524
01525 if (unit_in_cycle(it)) {
01526 gui_->scroll_to_tile(it->first,game_display::WARP);
01527 select_hex(it->first, browse);
01528 mouse_update(browse);
01529 }
01530 }
01531
01532 void mouse_handler::set_current_paths(paths new_paths) {
01533 gui_->unhighlight_reach();
01534 current_paths_ = new_paths;
01535 current_route_.steps.clear();
01536 gui_->set_route(NULL);
01537 }
01538
01539 mouse_handler *mouse_handler::singleton_ = NULL;
01540 }