00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "global.hpp"
00019
00020 #include "actions.hpp"
00021 #include "cursor.hpp"
00022 #include "game_display.hpp"
00023 #include "events.hpp"
00024 #include "filesystem.hpp"
00025 #include "font.hpp"
00026 #include "game_config.hpp"
00027 #include "gettext.hpp"
00028 #include "halo.hpp"
00029 #include "hotkeys.hpp"
00030 #include "language.hpp"
00031 #include "log.hpp"
00032 #include "marked-up_text.hpp"
00033 #include "minimap.hpp"
00034 #include "game_preferences.hpp"
00035 #include "gamestatus.hpp"
00036 #include "sdl_utils.hpp"
00037 #include "sound.hpp"
00038 #include "team.hpp"
00039 #include "theme.hpp"
00040 #include "tooltips.hpp"
00041 #include "unit_display.hpp"
00042 #include "util.hpp"
00043
00044 #include "SDL_image.h"
00045
00046 #include <algorithm>
00047 #include <cassert>
00048 #include <cmath>
00049 #include <iostream>
00050 #include <sstream>
00051
00052 #define ERR_DP LOG_STREAM(err, display)
00053 #define INFO_DP LOG_STREAM(info, display)
00054
00055 std::map<gamemap::location,fixed_t> game_display::debugHighlights_;
00056
00057 game_display::game_display(unit_map& units, CVideo& video, const gamemap& map,
00058 const gamestatus& status, const std::vector<team>& t,
00059 const config& theme_cfg, const config& cfg, const config& level) :
00060 display(video, map, theme_cfg, cfg, level),
00061 units_(units),
00062 temp_unit_(NULL),
00063 temp_unit_loc_(),
00064 attack_indicator_src_(),
00065 attack_indicator_dst_(),
00066 energy_bar_rects_(),
00067 route_(),
00068 status_(status),
00069 teams_(t),
00070 level_(level),
00071 invalidateUnit_(true),
00072 displayedUnitHex_(),
00073 overlays_(),
00074 currentTeam_(0),
00075 activeTeam_(0),
00076 sidebarScaling_(1.0),
00077 first_turn_(true),
00078 in_game_(false),
00079 observers_(),
00080 chat_messages_(),
00081 tod_hex_mask1(NULL),
00082 tod_hex_mask2(NULL),
00083 reach_map_(),
00084 reach_map_old_(),
00085 reach_map_changed_(true),
00086 game_mode_(RUNNING),
00087 flags_()
00088 {
00089 singleton_ = this;
00090
00091
00092 flags_.reserve(teams_.size());
00093
00094 std::vector<std::string> side_colors;
00095 side_colors.reserve(teams_.size());
00096
00097 for(size_t i = 0; i != teams_.size(); ++i) {
00098 std::string side_color = team::get_side_colour_index(i+1);
00099 side_colors.push_back(side_color);
00100 std::string flag = teams_[i].flag();
00101 std::string old_rgb = game_config::flag_rgb;
00102 std::string new_rgb = side_color;
00103
00104 if(flag.empty()) {
00105 flag = game_config::flag_image;
00106 }
00107
00108 LOG_STREAM(info, display) << "Adding flag for team " << i << " from animation " << flag << "\n";
00109
00110
00111 animated<image::locator> temp_anim;
00112
00113 std::vector<std::string> items = utils::split(flag);
00114 std::vector<std::string>::const_iterator itor = items.begin();
00115 for(; itor != items.end(); ++itor) {
00116 const std::vector<std::string>& items = utils::split(*itor, ':');
00117 std::string str;
00118 int time;
00119
00120 if(items.size() > 1) {
00121 str = items.front();
00122 time = atoi(items.back().c_str());
00123 } else {
00124 str = *itor;
00125 time = 100;
00126 }
00127 std::stringstream temp;
00128 temp << str << "~RC(" << old_rgb << ">"<< new_rgb << ")";
00129 image::locator flag_image(temp.str());
00130 temp_anim.add_frame(time, flag_image);
00131 }
00132 flags_.push_back(temp_anim);
00133
00134 flags_.back().start_animation(rand()%flags_.back().get_end_time(), true);
00135 }
00136 image::set_team_colors(&side_colors);
00137
00138
00139 surface const disp(screen_.getSurface());
00140 SDL_Rect area = screen_area();
00141 SDL_FillRect(disp,&area,SDL_MapRGB(disp->format,0,0,0));
00142 }
00143
00144 game_display::~game_display()
00145 {
00146
00147 prune_chat_messages(true);
00148 singleton_ = NULL;
00149 }
00150
00151 void game_display::new_turn()
00152 {
00153 const time_of_day& tod = status_.get_time_of_day();
00154
00155 if( !first_turn_) {
00156 image::set_image_mask("");
00157
00158 const time_of_day& old_tod = status_.get_previous_time_of_day();
00159
00160 if(old_tod.image_mask != tod.image_mask) {
00161 const surface old_mask(image::get_image(old_tod.image_mask,image::UNMASKED));
00162 const surface new_mask(image::get_image(tod.image_mask,image::UNMASKED));
00163
00164 const int niterations = static_cast<int>(10/turbo_speed());
00165 const int frame_time = 30;
00166 const int starting_ticks = SDL_GetTicks();
00167 for(int i = 0; i != niterations; ++i) {
00168
00169 if(old_mask != NULL) {
00170 const fixed_t proportion = ftofxp(1.0) - fxpdiv(i,niterations);
00171 tod_hex_mask1.assign(adjust_surface_alpha(old_mask,proportion));
00172 }
00173
00174 if(new_mask != NULL) {
00175 const fixed_t proportion = fxpdiv(i,niterations);
00176 tod_hex_mask2.assign(adjust_surface_alpha(new_mask,proportion));
00177 }
00178
00179 invalidate_all();
00180 draw();
00181
00182 const int cur_ticks = SDL_GetTicks();
00183 const int wanted_ticks = starting_ticks + i*frame_time;
00184 if(cur_ticks < wanted_ticks) {
00185 SDL_Delay(wanted_ticks - cur_ticks);
00186 }
00187 }
00188 }
00189
00190 tod_hex_mask1.assign(NULL);
00191 tod_hex_mask2.assign(NULL);
00192 }
00193
00194 first_turn_ = false;
00195
00196 image::set_colour_adjustment(tod.red,tod.green,tod.blue);
00197 image::set_image_mask(tod.image_mask);
00198
00199 invalidate_all();
00200 draw();
00201 }
00202
00203 void game_display::adjust_colours(int r, int g, int b)
00204 {
00205 const time_of_day& tod = status_.get_time_of_day();
00206 image::set_colour_adjustment(tod.red+r,tod.green+g,tod.blue+b);
00207 }
00208
00209 void game_display::select_hex(gamemap::location hex)
00210 {
00211 if(hex.valid() && fogged(hex)) {
00212 return;
00213 }
00214 display::select_hex(hex);
00215
00216 display_unit_hex(hex);
00217 }
00218
00219 void game_display::highlight_hex(gamemap::location hex)
00220 {
00221 unit_map::const_iterator u = find_visible_unit(units_,hex, map_, teams_,teams_[viewing_team()]);
00222 if (u != units_.end()) {
00223 displayedUnitHex_ = hex;
00224 invalidate_unit();
00225 } else {
00226 u = find_visible_unit(units_,mouseoverHex_, map_, teams_,teams_[viewing_team()]);
00227 if (u != units_.end()) {
00228
00229 if (units_.count(selectedHex_)) {
00230 displayedUnitHex_ = selectedHex_;
00231 invalidate_unit();
00232 }
00233 }
00234 }
00235
00236 display::highlight_hex(hex);
00237 invalidate_game_status();
00238 }
00239
00240
00241 void game_display::display_unit_hex(gamemap::location hex)
00242 {
00243 unit_map::const_iterator u = find_visible_unit(units_,hex, map_, teams_,teams_[viewing_team()]);
00244 if (u != units_.end()) {
00245 displayedUnitHex_ = hex;
00246 invalidate_unit();
00247 }
00248 }
00249
00250 void game_display::invalidate_unit_after_move(const gamemap::location& src, const gamemap::location& dst)
00251 {
00252 if (src == displayedUnitHex_) {
00253 displayedUnitHex_ = dst;
00254 invalidate_unit();
00255 }
00256 }
00257
00258 void game_display::scroll_to_leader(unit_map& units, int side, SCROLL_TYPE scroll_type)
00259 {
00260 const unit_map::iterator leader = find_leader(units,side);
00261
00262 if(leader != units_.end()) {
00263
00264
00265
00266
00267
00268 scroll_to_tile(leader->first, scroll_type);
00269 }
00270 }
00271
00272 void game_display::draw(bool update,bool force)
00273 {
00274 if (screen_.update_locked()) {
00275 return;
00276 }
00277
00278 bool changed = display::draw_init();
00279
00280
00281 invalidate_animations();
00282
00283 process_reachmap_changes();
00284
00285
00286
00287 prune_chat_messages();
00288
00289 if(map_.empty()) {
00290 display::draw_wrap(update, force, changed);
00291 return;
00292 }
00293
00294 halo::unrender(invalidated_);
00295
00296
00297 if(!invalidated_.empty()) {
00298 changed = true;
00299
00300
00301 std::set<gamemap::location, ordered_draw> unit_invals;
00302
00303 const time_of_day& tod = status_.get_time_of_day();
00304 const std::string shroud_image = "terrain/" +
00305 map_.get_terrain_info(t_translation::VOID_TERRAIN).minimap_image() + ".png";
00306 const std::string fog_image = "terrain/" +
00307 map_.get_terrain_info(t_translation::FOGGED).minimap_image() + ".png";
00308
00309 SDL_Rect clip_rect = map_area();
00310 surface screen = get_screen_surface();
00311 clip_rect_setter set_clip_rect(screen, clip_rect);
00312
00313 std::set<gamemap::location>::const_iterator it;
00314 for(it = invalidated_.begin(); it != invalidated_.end(); ++it) {
00315 int xpos = get_location_x(*it);
00316 int ypos = get_location_y(*it);
00317
00318 tblit blit(xpos, ypos);
00319 int drawing_order = gamemap::get_drawing_order(*it);
00320
00321
00322 if ((temp_unit_ && temp_unit_loc_==*it) || units_.find(*it) != units_.end()) {
00323 unit_invals.insert(*it);
00324 }
00325
00326 SDL_Rect hex_rect = {xpos, ypos, zoom_, zoom_};
00327 if(!rects_overlap(hex_rect,clip_rect)) {
00328 continue;
00329 }
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340 const bool on_map = map_.on_board(*it);
00341 const bool off_map_tile = (map_.get_terrain(*it) == t_translation::OFF_MAP_USER);
00342 const bool is_shrouded = shrouded(*it);
00343
00344 image::TYPE image_type = image::SCALED_TO_HEX;
00345
00346
00347
00348 if (on_map && (*it == mouseoverHex_ || *it == attack_indicator_src_)) {
00349 image_type = image::BRIGHTENED;
00350 } else if (on_map && *it == selectedHex_) {
00351 unit_map::iterator un = find_visible_unit(units_, *it, map_,
00352 teams_,teams_[currentTeam_]);
00353 if (un != units_.end()) {
00354 image_type = image::BRIGHTENED;
00355 }
00356 }
00357
00358
00359
00360
00361
00362
00363
00364
00365 if(!is_shrouded) {
00366
00367 drawing_buffer_add(LAYER_TERRAIN_BG, drawing_order, tblit(xpos, ypos,
00368 get_terrain_images(*it,tod.id, image_type, ADJACENT_BACKGROUND)));
00369
00370
00371 drawing_buffer_add(LAYER_TERRAIN_BG, drawing_order, tblit(xpos, ypos, get_flag(*it)));
00372
00373 typedef overlay_map::const_iterator Itor;
00374
00375 for(std::pair<Itor,Itor> overlays = overlays_.equal_range(*it);
00376 overlays.first != overlays.second; ++overlays.first) {
00377
00378 drawing_buffer_add(LAYER_TERRAIN_BG, drawing_order, tblit(xpos, ypos,
00379 image::get_image(overlays.first->second.image,image_type)));
00380 }
00381 }
00382
00383 if(!is_shrouded) {
00384
00385
00386
00387
00388 drawing_buffer_add(LAYER_TERRAIN_FG, drawing_order, tblit(xpos, ypos,
00389 get_terrain_images(*it,tod.id,image_type,ADJACENT_FOREGROUND)));
00390 }
00391
00392
00393
00394 std::string tod_hex_mask = timeofday_at(status_,units_,*it,map_).image_mask;
00395 if(tod_hex_mask1 != NULL || tod_hex_mask2 != NULL) {
00396 drawing_buffer_add(LAYER_TERRAIN_FG, drawing_order, tblit(xpos, ypos, tod_hex_mask1));
00397 drawing_buffer_add(LAYER_TERRAIN_FG, drawing_order, tblit(xpos, ypos, tod_hex_mask2));
00398 } else if(tod_hex_mask != "") {
00399 drawing_buffer_add(LAYER_TERRAIN_FG, drawing_order, tblit(xpos, ypos,
00400 image::get_image(tod_hex_mask,image::UNMASKED)));
00401 }
00402
00403
00404 if(grid_ && !is_shrouded && on_map && !off_map_tile) {
00405 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos,
00406 image::get_image(game_config::grid_image, image::SCALED_TO_HEX)));
00407 }
00408
00409
00410
00411
00412 if (!is_shrouded && !reach_map_.empty()
00413 && reach_map_.find(*it) == reach_map_.end() && *it != attack_indicator_dst_) {
00414 drawing_buffer_add(LAYER_TERRAIN_TMP_FG, drawing_order, tblit(xpos, ypos,
00415 image::get_image(game_config::unreachable_image,image::UNMASKED)));
00416 }
00417
00418
00419 if(game_config::debug && debugHighlights_.count(*it)) {
00420 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos,
00421 image::get_image(game_config::cross_image, image::SCALED_TO_HEX)));
00422 }
00423
00424
00425 if(!hex_overlay_.empty()) {
00426 std::map<gamemap::location, surface>::const_iterator itor = hex_overlay_.find(*it);
00427 if(itor != hex_overlay_.end())
00428 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos, itor->second));
00429 }
00430
00431
00432 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos, footsteps_images(*it)));
00433
00434
00435 if(*it == selectedHex_ && on_map && selected_hex_overlay_ != NULL) {
00436 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos, selected_hex_overlay_));
00437 }
00438 if(*it == mouseoverHex_ && on_map && mouseover_hex_overlay_ != NULL) {
00439 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos, mouseover_hex_overlay_));
00440 }
00441
00442
00443 if(on_map && *it == attack_indicator_src_) {
00444 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos,
00445 image::get_image("misc/attack-indicator-src-" + attack_indicator_direction() + ".png", image::UNMASKED)));
00446 } else if (on_map && *it == attack_indicator_dst_) {
00447 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos,
00448 image::get_image("misc/attack-indicator-dst-" + attack_indicator_direction() + ".png", image::UNMASKED)));
00449 }
00450
00451
00452 if(is_shrouded) {
00453
00454
00455 drawing_buffer_add(LAYER_TERRAIN_TMP_FG, drawing_order, tblit(xpos, ypos,
00456 image::get_image(shroud_image, image::SCALED_TO_HEX)));
00457 } else if(fogged(*it)) {
00458 drawing_buffer_add(LAYER_TERRAIN_TMP_FG, drawing_order, tblit(xpos, ypos,
00459 image::get_image(fog_image, image::SCALED_TO_HEX)));
00460 }
00461
00462
00463 if(game_mode_ != RUNNING) {
00464 blit.surf.push_back(image::get_image(game_config::linger_image, image::SCALED_TO_HEX));
00465 drawing_buffer_add(LAYER_LINGER_OVERLAY, drawing_order, blit);
00466 blit.surf.clear();
00467 }
00468
00469 if(!is_shrouded) {
00470 drawing_buffer_add(LAYER_TERRAIN_TMP_FG, drawing_order, tblit(xpos, ypos,
00471 get_terrain_images(*it, tod.id, image::SCALED_TO_HEX, ADJACENT_FOGSHROUD)));
00472 }
00473
00474
00475 if(!is_shrouded && on_map) {
00476 draw_movement_info(*it);
00477 }
00478
00479
00480
00481 if(!on_map && !off_map_tile) {
00482 draw_border(*it, xpos, ypos);
00483 }
00484 }
00485
00486
00487
00488 std::set<gamemap::location, struct display::ordered_draw>::const_iterator it2;
00489 for(it2 = unit_invals.begin(); it2 != unit_invals.end(); ++it2) {
00490 unit_map::iterator u_it = units_.find(*it2);
00491 if (u_it != units_.end()) {
00492 u_it->second.redraw_unit(*this, *it2);
00493
00494 }
00495
00496 if (temp_unit_ && temp_unit_loc_ == *it2) {
00497 temp_unit_->redraw_unit(*this, temp_unit_loc_, true);
00498
00499 }
00500 }
00501
00502 invalidated_.clear();
00503 }
00504
00505
00506 drawing_buffer_commit();
00507
00508 halo::render();
00509
00510 draw_sidebar();
00511
00512 changed = true;
00513
00514
00515
00516
00517 display::draw_wrap(update, force, changed);
00518 }
00519
00520 void game_display::draw_report(reports::TYPE report_num)
00521 {
00522 bool brighten;
00523
00524 if(!team_valid()) {
00525 return;
00526 }
00527
00528 reports::report report = reports::generate_report(report_num,report_,map_,
00529 units_, teams_,
00530 teams_[viewing_team()],
00531 size_t(currentTeam_+1),size_t(activeTeam_+1),
00532 selectedHex_,mouseoverHex_,displayedUnitHex_,status_,observers_,level_);
00533
00534 brighten = false;
00535 if(report_num == reports::TIME_OF_DAY) {
00536 time_of_day tod = timeofday_at(status_,units_,mouseoverHex_,map_);
00537
00538 if (teams_[viewing_team()].fogged(mouseoverHex_) ||
00539 teams_[viewing_team()].shrouded(mouseoverHex_)) {
00540
00541 tod = status_.get_time_of_day(false,mouseoverHex_);
00542 }
00543 brighten = (tod.bonus_modified > 0);
00544 }
00545
00546 refresh_report(report_num, report, brighten);
00547 }
00548
00549 void game_display::draw_game_status()
00550 {
00551 if(teams_.empty()) {
00552 return;
00553 }
00554
00555 for(size_t r = reports::STATUS_REPORTS_BEGIN; r != reports::STATUS_REPORTS_END; ++r) {
00556 draw_report(reports::TYPE(r));
00557 }
00558 }
00559
00560 void game_display::draw_sidebar()
00561 {
00562 draw_report(reports::REPORT_CLOCK);
00563 draw_report(reports::REPORT_COUNTDOWN);
00564
00565 if(teams_.empty()) {
00566 return;
00567 }
00568
00569 if(invalidateUnit_) {
00570
00571
00572 unit_map::const_iterator i =
00573 find_visible_unit(units_,displayedUnitHex_,
00574 map_,
00575 teams_,teams_[viewing_team()]);
00576
00577 if(i != units_.end()) {
00578 for(size_t r = reports::UNIT_REPORTS_BEGIN; r != reports::UNIT_REPORTS_END; ++r) {
00579 draw_report(reports::TYPE(r));
00580 }
00581 }
00582
00583 invalidateUnit_ = false;
00584 }
00585
00586 if(invalidateGameStatus_) {
00587 draw_game_status();
00588 invalidateGameStatus_ = false;
00589 }
00590 }
00591
00592 void game_display::draw_minimap_units()
00593 {
00594 double xscaling = 1.0 * minimap_location_.w / map_.w();
00595 double yscaling = 1.0 * minimap_location_.h / map_.h();
00596
00597 for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u) {
00598 if(fogged(u->first) ||
00599 (teams_[currentTeam_].is_enemy(u->second.side()) &&
00600 u->second.invisible(u->first,units_,teams_))) {
00601 continue;
00602 }
00603
00604 const int side = u->second.side();
00605 const SDL_Color col = team::get_minimap_colour(side);
00606 const Uint32 mapped_col = SDL_MapRGB(video().getSurface()->format,col.r,col.g,col.b);
00607
00608 double u_x = u->first.x * xscaling;
00609 double u_y = (u->first.y + (is_odd(u->first.x) ? 1 : -1)/4.0) * yscaling;
00610
00611 double u_w = 4.0 / 3.0 * xscaling;
00612 double u_h = yscaling;
00613
00614 SDL_Rect r = { minimap_location_.x + round_double(u_x),
00615 minimap_location_.y + round_double(u_y),
00616 round_double(u_w), round_double(u_h) };
00617
00618 SDL_FillRect(video().getSurface(), &r, mapped_col);
00619 }
00620 }
00621
00622 void game_display::draw_bar(const std::string& image, int xpos, int ypos,
00623 const int drawing_order, size_t height, double filled, const SDL_Color& col, fixed_t alpha)
00624 {
00625
00626 filled = minimum<double>(maximum<double>(filled,0.0),1.0);
00627 height = static_cast<size_t>(height*get_zoom_factor());
00628 #ifdef USE_TINY_GUI
00629 height /= 2;
00630 #endif
00631
00632 surface surf(image::get_image(image,image::UNMASKED));
00633
00634
00635
00636
00637 surface bar_surf(image::get_image(image));
00638 if(surf == NULL || bar_surf == NULL) {
00639 return;
00640 }
00641
00642
00643
00644 const SDL_Rect& unscaled_bar_loc = calculate_energy_bar(bar_surf);
00645
00646 SDL_Rect bar_loc;
00647 if (surf->w == bar_surf->w && surf->h == bar_surf->h)
00648 bar_loc = unscaled_bar_loc;
00649 else {
00650 const fixed_t xratio = fxpdiv(surf->w,bar_surf->w);
00651 const fixed_t yratio = fxpdiv(surf->h,bar_surf->h);
00652 const SDL_Rect scaled_bar_loc = {fxptoi(unscaled_bar_loc. x * xratio),
00653 fxptoi(unscaled_bar_loc. y * yratio + 127),
00654 fxptoi(unscaled_bar_loc. w * xratio + 255),
00655 fxptoi(unscaled_bar_loc. h * yratio + 255)};
00656 bar_loc = scaled_bar_loc;
00657 }
00658
00659 if(height > bar_loc.h) {
00660 height = bar_loc.h;
00661 }
00662
00663
00664
00665
00666
00667
00668
00669
00670 const size_t skip_rows = bar_loc.h - height;
00671
00672 SDL_Rect top = {0,0,surf->w,bar_loc.y};
00673 SDL_Rect bot = {0,bar_loc.y+skip_rows,surf->w,0};
00674 bot.h = surf->w - bot.y;
00675
00676 drawing_buffer_add(LAYER_UNIT_BAR, drawing_order, tblit(xpos, ypos, surf, top));
00677 drawing_buffer_add(LAYER_UNIT_BAR, drawing_order, tblit(xpos, ypos + top.h, surf, bot));
00678
00679 const size_t unfilled = static_cast<const size_t>(height*(1.0 - filled));
00680
00681 if(unfilled < height && alpha >= ftofxp(0.3)) {
00682 const Uint8 r_alpha = minimum<unsigned>(unsigned(fxpmult(alpha,255)),255);
00683 surface filled_surf = create_compatible_surface(bar_surf, bar_loc.w, height - unfilled);
00684 SDL_Rect filled_area = {0, 0, bar_loc.w, height-unfilled};
00685 SDL_FillRect(filled_surf,&filled_area,SDL_MapRGBA(bar_surf->format,col.r,col.g,col.b, r_alpha));
00686 drawing_buffer_add(LAYER_UNIT_BAR, drawing_order, tblit(xpos + bar_loc.x, ypos + bar_loc.y + unfilled, filled_surf));
00687 }
00688 }
00689
00690 void game_display::set_game_mode(const tgame_mode game_mode)
00691 {
00692 if(game_mode != game_mode_) {
00693 game_mode_ = game_mode;
00694 invalidate_all();
00695 }
00696 }
00697
00698 void game_display::draw_movement_info(const gamemap::location& loc)
00699 {
00700 const int drawing_order = gamemap::get_drawing_order(loc);
00701
00702
00703 std::map<gamemap::location, paths::route::waypoint>::iterator w = route_.waypoints.find(loc);
00704
00705
00706 if(w != route_.waypoints.end()
00707 && !route_.steps.empty() && route_.steps.front() != loc) {
00708 const unit_map::const_iterator un = units_.find(route_.steps.front());
00709 if(un != units_.end()) {
00710
00711 const int def = 100 - un->second.defense_modifier(map_.get_terrain(loc));
00712 std::stringstream def_text;
00713 def_text << def << "%";
00714
00715
00716 int val = (game_config::defense_color_scale.size()-1) * def/100;
00717 SDL_Color color = int_to_color(game_config::defense_color_scale[val]);
00718 draw_text_in_hex(loc, LAYER_TERRAIN_TMP_BG, def_text.str(), 18, color);
00719
00720 int xpos = get_location_x(loc);
00721 int ypos = get_location_y(loc);
00722
00723 if (w->second.invisible) {
00724 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos,
00725 image::get_image("misc/hidden.png", image::UNMASKED)));
00726 }
00727
00728 if (w->second.zoc) {
00729 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos,
00730 image::get_image("misc/zoc.png", image::UNMASKED)));
00731 }
00732
00733 if (w->second.capture) {
00734 drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos,
00735 image::get_image("misc/capture.png", image::UNMASKED)));
00736 }
00737
00738
00739 if (w->second.turns > 1 || loc != route_.steps.back()) {
00740 std::stringstream turns_text;
00741 turns_text << w->second.turns;
00742 draw_text_in_hex(loc, LAYER_TERRAIN_TMP_BG, turns_text.str(), 17, font::NORMAL_COLOUR, 0.5,0.8);
00743 }
00744
00745 return;
00746 }
00747 }
00748
00749 if (!reach_map_.empty()) {
00750 reach_map::iterator reach = reach_map_.find(loc);
00751 if (reach != reach_map_.end() && reach->second > 1) {
00752 const std::string num = lexical_cast<std::string>(reach->second);
00753 draw_text_in_hex(loc, LAYER_TERRAIN_TMP_BG, num, 16, font::YELLOW_COLOUR);
00754 }
00755 }
00756 }
00757
00758 std::vector<surface> game_display::footsteps_images(const gamemap::location& loc)
00759 {
00760 std::vector<surface> res;
00761
00762 if (route_.steps.size() < 2) {
00763 return res;
00764 }
00765
00766 std::vector<gamemap::location>::const_iterator i =
00767 std::find(route_.steps.begin(),route_.steps.end(),loc);
00768
00769 if( i == route_.steps.end()) {
00770 return res;
00771 }
00772
00773
00774 int move_cost = 1;
00775 const unit_map::const_iterator u = units_.find(route_.steps.front());
00776 if(u != units_.end()) {
00777 move_cost = u->second.movement_cost(map_.get_terrain(loc));
00778 }
00779 int image_number = minimum<int>(move_cost, game_config::foot_speed_prefix.size());
00780 if (image_number < 1) {
00781 return res;
00782 }
00783 const std::string foot_speed_prefix = game_config::foot_speed_prefix[image_number-1];
00784
00785 surface teleport = NULL;
00786
00787
00788
00789 const int first_half = (i == route_.steps.begin()) ? 1 : 0;
00790
00791 const int second_half = (i+1 == route_.steps.end()) ? 0 : 1;
00792
00793 for (int h = first_half; h <= second_half; h++) {
00794 const std::string sense( h==0 ? "-in" : "-out" );
00795
00796 if (!tiles_adjacent(*(i-1+h), *(i+h))) {
00797 std::string teleport_image =
00798 h==0 ? game_config::foot_teleport_enter : game_config::foot_teleport_exit;
00799 teleport = image::get_image(teleport_image, image::UNMASKED);
00800 continue;
00801 }
00802
00803
00804 gamemap::location::DIRECTION dir = (i-1+h)->get_relative_dir(*(i+h));
00805
00806 std::string rotate;
00807 if (dir > gamemap::location::SOUTH_EAST) {
00808
00809 dir = i->get_opposite_dir(dir);
00810 rotate = "~FL(horiz)~FL(vert)";
00811 }
00812
00813 const std::string image = foot_speed_prefix
00814 + sense + "-" + i->write_direction(dir)
00815 + ".png" + rotate;
00816
00817 res.push_back(image::get_image(image, image::UNMASKED));
00818 }
00819
00820
00821 if (teleport != NULL) res.push_back(teleport);
00822
00823 return res;
00824 }
00825
00826 surface game_display::get_flag(const gamemap::location& loc)
00827 {
00828 t_translation::t_terrain terrain = map_.get_terrain(loc);
00829
00830 if(!map_.is_village(terrain)) {
00831 return surface(NULL);
00832 }
00833
00834 for(size_t i = 0; i != teams_.size(); ++i) {
00835 if(teams_[i].owns_village(loc) &&
00836 (!fogged(loc) || !teams_[currentTeam_].is_enemy(i+1)))
00837 {
00838 flags_[i].update_last_draw_time();
00839 image::locator image_flag = preferences::animate_map() ?
00840 flags_[i].get_current_frame() : flags_[i].get_first_frame();
00841 return image::get_image(image_flag, image::SCALED_TO_HEX);
00842 }
00843 }
00844
00845 return surface(NULL);
00846 }
00847
00848 void game_display::highlight_reach(const paths &paths_list)
00849 {
00850 unhighlight_reach();
00851 highlight_another_reach(paths_list);
00852 }
00853
00854 void game_display::highlight_another_reach(const paths &paths_list)
00855 {
00856 paths::routes_map::const_iterator r;
00857
00858
00859 for (r = paths_list.routes.begin(); r != paths_list.routes.end(); ++r) {
00860 reach_map_[r->first]++;
00861 }
00862 reach_map_changed_ = true;
00863 }
00864
00865 void game_display::unhighlight_reach()
00866 {
00867 reach_map_ = reach_map();
00868 reach_map_changed_ = true;
00869 }
00870
00871 void game_display::process_reachmap_changes()
00872 {
00873 if (!reach_map_changed_) return;
00874 if (reach_map_.empty() != reach_map_old_.empty()) {
00875
00876 reach_map &full = reach_map_.empty() ? reach_map_old_ : reach_map_;
00877 gamemap::location topleft;
00878 gamemap::location bottomright;
00879 get_visible_hex_bounds(topleft, bottomright);
00880 for(int x = topleft.x; x <= bottomright.x; ++x) {
00881 for(int y = topleft.y; y <= bottomright.y; ++y) {
00882 gamemap::location loc(x, y);
00883 reach_map::iterator reach = full.find(loc);
00884 if (reach == full.end()) {
00885
00886 invalidate(loc);
00887 } else if (reach->second != 1) {
00888
00889 invalidate(loc);
00890 }
00891 }
00892 }
00893 } else if (!reach_map_.empty()) {
00894
00895 reach_map::iterator reach, reach_old;
00896 for (reach = reach_map_.begin(); reach != reach_map_.end(); ++reach) {
00897 reach_old = reach_map_old_.find(reach->first);
00898 if (reach_old == reach_map_old_.end()) {
00899 invalidate(reach->first);
00900 } else {
00901 if (reach_old->second != reach->second) {
00902 invalidate(reach->first);
00903 }
00904 reach_map_old_.erase(reach_old);
00905 }
00906 }
00907 for (reach_old = reach_map_old_.begin(); reach_old != reach_map_old_.end(); ++reach_old) {
00908 invalidate(reach_old->first);
00909 }
00910 }
00911 reach_map_old_ = reach_map_;
00912 reach_map_changed_ = false;
00913 }
00914
00915 void game_display::invalidate_route()
00916 {
00917 for(std::vector<gamemap::location>::const_iterator i = route_.steps.begin();
00918 i != route_.steps.end(); ++i) {
00919 invalidate(*i);
00920 }
00921 }
00922
00923 void game_display::set_route(const paths::route* route)
00924 {
00925 invalidate_route();
00926
00927 if(route != NULL) {
00928 route_ = *route;
00929 } else {
00930 route_.steps.clear();
00931 route_.waypoints.clear();
00932 }
00933
00934 invalidate_route();
00935 }
00936
00937 void game_display::float_label(const gamemap::location& loc, const std::string& text,
00938 int red, int green, int blue)
00939 {
00940 if(preferences::show_floating_labels() == false || fogged(loc)) {
00941 return;
00942 }
00943
00944 const SDL_Color color = {red,green,blue,255};
00945 int lifetime = static_cast<int>(60/turbo_speed());
00946 font::add_floating_label(text,font::SIZE_XLARGE,color,get_location_x(loc)+zoom_/2,get_location_y(loc),
00947 0,-2*turbo_speed(),lifetime,screen_area(),font::CENTER_ALIGN,NULL,0,font::ANCHOR_LABEL_MAP);
00948 }
00949
00950 struct is_energy_colour {
00951 bool operator()(Uint32 colour) const { return (colour&0xFF000000) > 0x10000000 &&
00952 (colour&0x00FF0000) < 0x00100000 &&
00953 (colour&0x0000FF00) < 0x00001000 &&
00954 (colour&0x000000FF) < 0x00000010; }
00955 };
00956
00957 const SDL_Rect& game_display::calculate_energy_bar(surface surf)
00958 {
00959 const std::map<surface,SDL_Rect>::const_iterator i = energy_bar_rects_.find(surf);
00960 if(i != energy_bar_rects_.end()) {
00961 return i->second;
00962 }
00963
00964 int first_row = -1, last_row = -1, first_col = -1, last_col = -1;
00965
00966 surface image(make_neutral_surface(surf));
00967
00968 surface_lock image_lock(image);
00969 const Uint32* const begin = image_lock.pixels();
00970
00971 for(int y = 0; y != image->h; ++y) {
00972 const Uint32* const i1 = begin + image->w*y;
00973 const Uint32* const i2 = i1 + image->w;
00974 const Uint32* const itor = std::find_if(i1,i2,is_energy_colour());
00975 const int count = std::count_if(itor,i2,is_energy_colour());
00976
00977 if(itor != i2) {
00978 if(first_row == -1) {
00979 first_row = y;
00980 }
00981
00982 first_col = itor - i1;
00983 last_col = first_col + count;
00984 last_row = y;
00985 }
00986 }
00987
00988 const SDL_Rect res = {first_col,first_row,last_col-first_col,last_row+1-first_row};
00989 energy_bar_rects_.insert(std::pair<surface,SDL_Rect>(surf,res));
00990 return calculate_energy_bar(surf);
00991 }
00992
00993 void game_display::invalidate(const gamemap::location& loc)
00994 {
00995 if(!invalidateAll_) {
00996 if (invalidated_.insert(loc).second) {
00997
00998 unit_map::iterator u = units_.find(loc);
00999
01000 if (u != units_.end()) {
01001 std::set<gamemap::location> overlaps = u->second.overlaps(u->first);
01002 for (std::set<gamemap::location>::iterator i = overlaps.begin(); i != overlaps.end(); i++) {
01003 invalidate(*i);
01004 }
01005 }
01006 if (temp_unit_ && temp_unit_loc_ == loc ) {
01007 std::set<gamemap::location> overlaps = temp_unit_->overlaps(temp_unit_loc_);
01008 for (std::set<gamemap::location>::iterator i = overlaps.begin(); i != overlaps.end(); i++) {
01009 invalidate(*i);
01010 }
01011 }
01012
01013 gamemap::location adjacent[6];
01014 get_adjacent_tiles(loc, adjacent);
01015 for (unsigned int i = 0; i < 6; i++) {
01016 u = units_.find(adjacent[i]);
01017 if (u != units_.end()) {
01018 std::set<gamemap::location> overlaps = u->second.overlaps(u->first);
01019 if (overlaps.find(loc) != overlaps.end()) {
01020 invalidate(u->first);
01021 }
01022 }
01023 if (temp_unit_ && temp_unit_loc_ == adjacent[i] ) {
01024 std::set<gamemap::location> overlaps = temp_unit_->overlaps(temp_unit_loc_);
01025 if (overlaps.find(loc) != overlaps.end()) {
01026 invalidate(temp_unit_loc_);
01027 }
01028 }
01029 }
01030 }
01031 }
01032 }
01033
01034 void game_display::invalidate_animations()
01035 {
01036 new_animation_frame();
01037
01038 unit_map::iterator unit;
01039 for(unit=units_.begin() ; unit != units_.end() ; unit++) {
01040 unit->second.refresh(*this, unit->first);
01041 if (unit->second.get_animation() && unit->second.get_animation()->need_update())
01042 invalidate(unit->first);
01043 }
01044 if (temp_unit_ ) {
01045 temp_unit_->refresh(*this, temp_unit_loc_);
01046 if (temp_unit_->get_animation() && temp_unit_->get_animation()->need_update())
01047 invalidate(temp_unit_loc_);
01048 }
01049
01050 if (!preferences::animate_map()) {return;}
01051
01052 gamemap::location topleft;
01053 gamemap::location bottomright;
01054 get_visible_hex_bounds(topleft, bottomright);
01055
01056 for(int x = topleft.x; x <= bottomright.x; ++x) {
01057 for(int y = topleft.y; y <= bottomright.y; ++y) {
01058 const gamemap::location loc(x,y);
01059 if (!shrouded(loc)) {
01060 if (builder_.update_animation(loc)) {
01061 invalidate(loc);
01062 } else if (map_.is_village(loc)) {
01063 const int owner = player_teams::village_owner(loc);
01064 if (owner >= 0 && flags_[owner].need_update() && (!fogged(loc) || !teams_[currentTeam_].is_enemy(owner+1)))
01065 invalidate(loc);
01066 }
01067 }
01068 }
01069 }
01070 }
01071
01072 void game_display::debug_highlight(const gamemap::location& loc, fixed_t amount)
01073 {
01074 assert(game_config::debug);
01075 debugHighlights_[loc] += amount;
01076 }
01077
01078 void game_display::place_temporary_unit(unit &u, const gamemap::location& loc)
01079 {
01080 temp_unit_ = &u;
01081 temp_unit_loc_ = loc;
01082 invalidate(loc);
01083 }
01084
01085 void game_display::remove_temporary_unit()
01086 {
01087 if(!temp_unit_) return;
01088
01089 invalidate(temp_unit_loc_);
01090
01091 temp_unit_->clear_haloes();
01092 temp_unit_ = NULL;
01093 }
01094
01095 void game_display::set_attack_indicator(const gamemap::location& src, const gamemap::location& dst)
01096 {
01097 if (attack_indicator_src_ != src || attack_indicator_dst_ != dst) {
01098 invalidate(attack_indicator_src_);
01099 invalidate(attack_indicator_dst_);
01100
01101 attack_indicator_src_ = src;
01102 attack_indicator_dst_ = dst;
01103
01104 invalidate(attack_indicator_src_);
01105 invalidate(attack_indicator_dst_);
01106 }
01107 }
01108
01109 void game_display::clear_attack_indicator()
01110 {
01111 set_attack_indicator(gamemap::location::null_location, gamemap::location::null_location);
01112 }
01113
01114 void game_display::add_overlay(const gamemap::location& loc, const std::string& img, const std::string& halo)
01115 {
01116 const int halo_handle = halo::add(get_location_x(loc) + hex_size() / 2,
01117 get_location_y(loc) + hex_size() / 2, halo, loc);
01118
01119 const overlay item(img,halo,halo_handle);
01120 overlays_.insert(overlay_map::value_type(loc,item));
01121 }
01122
01123 void game_display::remove_overlay(const gamemap::location& loc)
01124 {
01125 typedef overlay_map::const_iterator Itor;
01126 std::pair<Itor,Itor> itors = overlays_.equal_range(loc);
01127 while(itors.first != itors.second) {
01128 halo::remove(itors.first->second.halo_handle);
01129 ++itors.first;
01130 }
01131
01132 overlays_.erase(loc);
01133 }
01134
01135 void game_display::remove_single_overlay(const gamemap::location& loc, const std::string& toDelete)
01136 {
01137
01138 typedef overlay_map::iterator Itor;
01139 overlay_map::iterator iteratorCopy;
01140 std::pair<Itor,Itor> itors = overlays_.equal_range(loc);
01141 while(itors.first != itors.second) {
01142
01143 if(itors.first->second.image == toDelete || itors.first->second.halo == toDelete) {
01144 iteratorCopy = itors.first;
01145 ++itors.first;
01146 halo::remove(iteratorCopy->second.halo_handle);
01147 overlays_.erase(iteratorCopy);
01148 }
01149 else {
01150 ++itors.first;
01151 }
01152 }
01153 }
01154
01155 void game_display::write_overlays(config& cfg) const
01156 {
01157 for(overlay_map::const_iterator i = overlays_.begin(); i != overlays_.end(); ++i) {
01158 config& item = cfg.add_child("item");
01159 i->first.write(item);
01160 item["image"] = i->second.image;
01161 item["halo"] = i->second.halo;
01162 }
01163 }
01164
01165 const std::string game_display::current_team_name() const
01166 {
01167 if (team_valid())
01168 {
01169 return teams_[currentTeam_].team_name();
01170 }
01171 return std::string();
01172 }
01173
01174 void game_display::set_team(size_t teamindex, bool observe)
01175 {
01176 assert(teamindex < teams_.size());
01177 currentTeam_ = teamindex;
01178 if (!observe)
01179 {
01180 labels().set_team(&teams_[teamindex]);
01181 viewpoint_ = &teams_[teamindex];
01182 }
01183 else
01184 {
01185 labels().set_team(0);
01186 viewpoint_ = NULL;
01187 }
01188 labels().recalculate_labels();
01189 }
01190
01191 void game_display::set_playing_team(size_t teamindex)
01192 {
01193 assert(teamindex < teams_.size());
01194 activeTeam_ = teamindex;
01195 invalidate_game_status();
01196 }
01197
01198 void game_display::begin_game()
01199 {
01200 in_game_ = true;
01201 create_buttons();
01202 invalidate_all();
01203 }
01204
01205 namespace {
01206 const int chat_message_border = 5;
01207 const int chat_message_x = 10;
01208 const int chat_message_y = 10;
01209 const SDL_Color chat_message_colour = {255,255,255,255};
01210 const SDL_Color chat_message_bg = {0,0,0,140};
01211 }
01212
01213 void game_display::add_chat_message(const time_t& time, const std::string& speaker,
01214 int side, const std::string& message, game_display::MESSAGE_TYPE type,
01215 bool bell)
01216 {
01217 const bool whisper = speaker.find("whisper: ") == 0;
01218 std::string sender = speaker;
01219 if (whisper) {
01220 sender.assign(speaker, 9, speaker.size());
01221 }
01222 if (!preferences::show_lobby_join(sender, message)) return;
01223 if (preferences::is_ignored(sender)) return;
01224
01225 if (bell) {
01226 if ((type == MESSAGE_PRIVATE && (!is_observer() || whisper))
01227 || utils::word_match(message, preferences::login())) {
01228 sound::play_UI_sound(game_config::sounds::receive_message_highlight);
01229 } else if (preferences::is_friend(sender)) {
01230 sound::play_UI_sound(game_config::sounds::receive_message_friend);
01231 } else if (sender == "server") {
01232 sound::play_UI_sound(game_config::sounds::receive_message_server);
01233 } else {
01234 sound::play_UI_sound(game_config::sounds::receive_message);
01235 }
01236 }
01237
01238 bool action = false;
01239 std::string msg;
01240 if (message.find("/me ") == 0) {
01241 msg.assign(message, 4, message.size());
01242 action = true;
01243 } else {
01244 msg = message;
01245 }
01246
01247 try {
01248
01249
01250 msg = font::word_wrap_text(msg,font::SIZE_SMALL,map_outside_area().w*3/4);
01251 } catch (utils::invalid_utf8_exception&) {
01252 LOG_STREAM(err, engine) << "Invalid utf-8 found, chat message is ignored.\n";
01253 return;
01254 }
01255
01256 int ypos = chat_message_x;
01257 for(std::vector<chat_message>::const_iterator m = chat_messages_.begin(); m != chat_messages_.end(); ++m) {
01258 ypos += font::get_floating_label_rect(m->handle).h;
01259 }
01260 SDL_Color speaker_colour = {255,255,255,255};
01261 if(side >= 1) {
01262 speaker_colour = int_to_color(team::get_side_color_range(side).mid());
01263 }
01264
01265 SDL_Color message_colour = chat_message_colour;
01266 std::stringstream str;
01267 std::stringstream message_str;
01268 if(type == MESSAGE_PUBLIC) {
01269 if(action) {
01270 str << "<" << speaker << " " << msg << ">";
01271 message_colour = speaker_colour;
01272 message_str << " ";
01273 } else {
01274 if (!speaker.empty())
01275 str << "<" << speaker << ">";
01276 message_str << msg;
01277 }
01278 } else {
01279 if(action) {
01280 str << "*" << speaker << " " << msg << "*";
01281 message_colour = speaker_colour;
01282 message_str << " ";
01283 } else {
01284 if (!speaker.empty())
01285 str << "*" << speaker << "*";
01286 message_str << msg;
01287 }
01288 }
01289
01290
01291 std::stringstream message_complete;
01292 message_complete << preferences::get_chat_timestamp(time);
01293 message_complete << str.str();
01294
01295 const SDL_Rect rect = map_outside_area();
01296 const int speaker_handle = font::add_floating_label(message_complete.str(),font::SIZE_SMALL,speaker_colour,
01297 rect.x+chat_message_x,rect.y+ypos,
01298 0,0,-1,rect,font::LEFT_ALIGN,&chat_message_bg,chat_message_border);
01299
01300 const int message_handle = font::add_floating_label(message_str.str(),font::SIZE_SMALL,message_colour,
01301 rect.x + chat_message_x + font::get_floating_label_rect(speaker_handle).w,rect.y+ypos,
01302 0,0,-1,rect,font::LEFT_ALIGN,&chat_message_bg,chat_message_border);
01303
01304 chat_messages_.push_back(chat_message(speaker_handle,message_handle));
01305
01306 prune_chat_messages();
01307 }
01308
01309 void game_display::prune_chat_messages(bool remove_all)
01310 {
01311 const unsigned int message_ttl = remove_all ? 0 : 1200000;
01312 const unsigned int max_chat_messages = preferences::chat_lines();
01313 if(chat_messages_.empty() == false && (chat_messages_.front().created_at+message_ttl < SDL_GetTicks() || chat_messages_.size() > max_chat_messages)) {
01314 const int movement = font::get_floating_label_rect(chat_messages_.front().handle).h;
01315
01316 font::remove_floating_label(chat_messages_.front().speaker_handle);
01317 font::remove_floating_label(chat_messages_.front().handle);
01318 chat_messages_.erase(chat_messages_.begin());
01319
01320 for(std::vector<chat_message>::const_iterator i = chat_messages_.begin(); i != chat_messages_.end(); ++i) {
01321 font::move_floating_label(i->speaker_handle,0,-movement);
01322 font::move_floating_label(i->handle,0,-movement);
01323 }
01324
01325 prune_chat_messages(remove_all);
01326 }
01327 }
01328
01329 game_display *game_display::singleton_ = NULL;
01330