game_display.cpp

Go to the documentation of this file.
00001 /* $Id: game_display.cpp 48153 2011-01-01 15:57:50Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2011 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * During a game, show map & info-panels at top+right.
00019  */
00020 
00021 #include "global.hpp"
00022 
00023 #include "game_display.hpp"
00024 
00025 #include "wesconfig.h"
00026 
00027 #ifdef HAVE_LIBDBUS
00028 #include <dbus/dbus.h>
00029 #endif
00030 
00031 #ifdef HAVE_GROWL
00032 #include <Growl/GrowlApplicationBridge-Carbon.h>
00033 #include <Carbon/Carbon.h>
00034 Growl_Delegate growl_obj;
00035 #endif
00036 
00037 #include "foreach.hpp"
00038 #include "game_preferences.hpp"
00039 #include "halo.hpp"
00040 #include "log.hpp"
00041 #include "map.hpp"
00042 #include "map_label.hpp"
00043 #include "marked-up_text.hpp"
00044 #include "reports.hpp"
00045 #include "resources.hpp"
00046 #include "tod_manager.hpp"
00047 #include "sound.hpp"
00048 #include "whiteboard/manager.hpp"
00049 
00050 static lg::log_domain log_display("display");
00051 #define ERR_DP LOG_STREAM(err, log_display)
00052 #define LOG_DP LOG_STREAM(info, log_display)
00053 
00054 static lg::log_domain log_engine("engine");
00055 #define ERR_NG LOG_STREAM(err, log_engine)
00056 
00057 std::map<map_location,fixed_t> game_display::debugHighlights_;
00058 
00059 game_display::game_display(unit_map& units, CVideo& video, const gamemap& map,
00060         const tod_manager& tod, const std::vector<team>& t,
00061         const config& theme_cfg, const config& level) :
00062         display(video, &map, theme_cfg, level),
00063         units_(units),
00064         temp_units_(),
00065         exclusive_unit_draw_requests_(),
00066         attack_indicator_src_(),
00067         attack_indicator_dst_(),
00068         energy_bar_rects_(),
00069         route_(),
00070         tod_manager_(tod),
00071         teams_(t),
00072         level_(level),
00073         displayedUnitHex_(),
00074         overlays_(),
00075         currentTeam_(0),
00076         activeTeam_(0),
00077         sidebarScaling_(1.0),
00078         first_turn_(true),
00079         in_game_(false),
00080         observers_(),
00081         chat_messages_(),
00082         reach_map_(),
00083         reach_map_old_(),
00084         reach_map_changed_(true),
00085         game_mode_(RUNNING),
00086         flags_()
00087 {
00088     singleton_ = this;
00089 
00090     // Inits the flag list and the team colors used by ~TC
00091     flags_.reserve(teams_.size());
00092 
00093     std::vector<std::string> side_colors;
00094     side_colors.reserve(teams_.size());
00095 
00096     for(size_t i = 0; i != teams_.size(); ++i) {
00097         std::string side_color = team::get_side_color_index(i+1);
00098         side_colors.push_back(side_color);
00099         std::string flag = teams_[i].flag();
00100         std::string old_rgb = game_config::flag_rgb;
00101         std::string new_rgb = side_color;
00102 
00103         if(flag.empty()) {
00104             flag = game_config::images::flag;
00105         }
00106 
00107         LOG_DP << "Adding flag for team " << i << " from animation " << flag << "\n";
00108 
00109         // Must recolor flag image
00110         animated<image::locator> temp_anim;
00111 
00112         std::vector<std::string> items = utils::split(flag);
00113         std::vector<std::string>::const_iterator itor = items.begin();
00114         for(; itor != items.end(); ++itor) {
00115             const std::vector<std::string>& items = utils::split(*itor, ':');
00116             std::string str;
00117             int time;
00118 
00119             if(items.size() > 1) {
00120                 str = items.front();
00121                 time = atoi(items.back().c_str());
00122             } else {
00123                 str = *itor;
00124                 time = 100;
00125             }
00126             std::stringstream temp;
00127             temp << str << "~RC(" << old_rgb << ">"<< new_rgb << ")";
00128             image::locator flag_image(temp.str());
00129             temp_anim.add_frame(time, flag_image);
00130         }
00131         flags_.push_back(temp_anim);
00132 
00133         flags_.back().start_animation(rand()%flags_.back().get_end_time(), true);
00134     }
00135     image::set_team_colors(&side_colors);
00136     clear_screen();
00137 }
00138 
00139 game_display* game_display::create_dummy_display(CVideo& video)
00140 {
00141     static unit_map dummy_umap;
00142     static config dummy_cfg;
00143     static gamemap dummy_map(dummy_cfg, "");
00144     static tod_manager dummy_tod(dummy_cfg, 0);
00145     static std::vector<team> dummy_teams;
00146     return new game_display(dummy_umap, video, dummy_map, dummy_tod,
00147             dummy_teams, dummy_cfg, dummy_cfg);
00148 }
00149 
00150 game_display::~game_display()
00151 {
00152     // SDL_FreeSurface(minimap_);
00153     prune_chat_messages(true);
00154     singleton_ = NULL;
00155 }
00156 
00157 void game_display::new_turn()
00158 {
00159     const time_of_day& tod = tod_manager_.get_time_of_day();
00160 
00161     if( !first_turn_) {
00162         const time_of_day& old_tod = tod_manager_.get_previous_time_of_day();
00163 
00164         if(old_tod.image_mask != tod.image_mask) {
00165             const surface old_mask(image::get_image(old_tod.image_mask,image::SCALED_TO_HEX));
00166             const surface new_mask(image::get_image(tod.image_mask,image::SCALED_TO_HEX));
00167 
00168             const int niterations = static_cast<int>(10/turbo_speed());
00169             const int frame_time = 30;
00170             const int starting_ticks = SDL_GetTicks();
00171             for(int i = 0; i != niterations; ++i) {
00172 
00173                 if(old_mask != NULL) {
00174                     const fixed_t proportion = ftofxp(1.0) - fxpdiv(i,niterations);
00175                     tod_hex_mask1.assign(adjust_surface_alpha(old_mask,proportion));
00176                 }
00177 
00178                 if(new_mask != NULL) {
00179                     const fixed_t proportion = fxpdiv(i,niterations);
00180                     tod_hex_mask2.assign(adjust_surface_alpha(new_mask,proportion));
00181                 }
00182 
00183                 invalidate_all();
00184                 draw();
00185 
00186                 const int cur_ticks = SDL_GetTicks();
00187                 const int wanted_ticks = starting_ticks + i*frame_time;
00188                 if(cur_ticks < wanted_ticks) {
00189                     SDL_Delay(wanted_ticks - cur_ticks);
00190                 }
00191             }
00192         }
00193 
00194         tod_hex_mask1.assign(NULL);
00195         tod_hex_mask2.assign(NULL);
00196     }
00197 
00198     first_turn_ = false;
00199 
00200     image::set_color_adjustment(tod.red,tod.green,tod.blue);
00201 
00202     invalidate_all();
00203     draw();
00204 }
00205 
00206 void game_display::adjust_colors(int r, int g, int b)
00207 {
00208     const time_of_day& tod = tod_manager_.get_time_of_day();
00209     image::set_color_adjustment(tod.red+r,tod.green+g,tod.blue+b);
00210 }
00211 
00212 void game_display::select_hex(map_location hex)
00213 {
00214     if(hex.valid() && fogged(hex)) {
00215         return;
00216     }
00217     display::select_hex(hex);
00218 
00219     display_unit_hex(hex);
00220 }
00221 
00222 void game_display::highlight_hex(map_location hex)
00223 {
00224     wb::scoped_planned_pathfind_map future; //< Lasts for whole method.
00225 
00226     const unit *u = get_visible_unit(hex, teams_[viewing_team()], !viewpoint_);
00227     if (u) {
00228         displayedUnitHex_ = hex;
00229         invalidate_unit();
00230     } else {
00231         u = get_visible_unit(mouseoverHex_, teams_[viewing_team()], !viewpoint_);
00232         if (u) {
00233             // mouse moved from unit hex to non-unit hex
00234             if (units_.count(selectedHex_)) {
00235                 displayedUnitHex_ = selectedHex_;
00236                 invalidate_unit();
00237             }
00238         }
00239     }
00240 
00241     display::highlight_hex(hex);
00242     invalidate_game_status();
00243 }
00244 
00245 
00246 void game_display::display_unit_hex(map_location hex)
00247 {
00248     wb::scoped_planned_pathfind_map future; //< Lasts for whole method.
00249 
00250     const unit *u = get_visible_unit(hex, teams_[viewing_team()], !viewpoint_);
00251     if (u) {
00252         displayedUnitHex_ = hex;
00253         invalidate_unit();
00254     }
00255 }
00256 
00257 void game_display::invalidate_unit_after_move(const map_location& src, const map_location& dst)
00258 {
00259     if (src == displayedUnitHex_) {
00260         displayedUnitHex_ = dst;
00261         invalidate_unit();
00262     }
00263 }
00264 
00265 void game_display::scroll_to_leader(unit_map& units, int side, SCROLL_TYPE scroll_type,bool force)
00266 {
00267     unit_map::const_iterator leader = units.find_leader(side);
00268 
00269     if(leader != units_.end()) {
00270         // YogiHH: I can't see why we need another key_handler here,
00271         // therefore I will comment it out :
00272         /*
00273         const hotkey::basic_handler key_events_handler(gui_);
00274         */
00275         scroll_to_tile(leader->get_location(), scroll_type, true, force);
00276     }
00277 }
00278 
00279 void game_display::pre_draw() {
00280     process_reachmap_changes();
00281     /**
00282      * @todo FIXME: must modify changed, but best to do it at the
00283      * floating_label level
00284      */
00285     prune_chat_messages();
00286 }
00287 
00288 image::TYPE game_display::get_image_type(const map_location& loc) {
00289     // We highlight hex under the mouse, or under a selected unit.
00290     if (get_map().on_board(loc)) {
00291         if (loc == mouseoverHex_ || loc == attack_indicator_src_) {
00292             return image::BRIGHTENED;
00293         } else if (loc == selectedHex_) {
00294             const unit *un = get_visible_unit(loc, teams_[currentTeam_], !viewpoint_);
00295             if (un && !un->get_hidden()) {
00296                 return image::BRIGHTENED;
00297             }
00298         }
00299     }
00300     return image::TOD_COLORED;
00301 }
00302 
00303 
00304 void game_display::draw_invalidated()
00305 {
00306     halo::unrender(invalidated_);
00307     display::draw_invalidated();
00308 
00309     foreach(unit* temp_unit, temp_units_) {
00310         const map_location& loc = temp_unit->get_location();
00311         exclusive_unit_draw_requests_t::iterator request = exclusive_unit_draw_requests_.find(loc);
00312         if (invalidated_.find(loc) != invalidated_.end()
00313                 && (request == exclusive_unit_draw_requests_.end() || request->second == temp_unit->id()))
00314             temp_unit->redraw_unit();
00315     }
00316 
00317     foreach (const map_location& loc, invalidated_) {
00318         unit_map::iterator u_it = units_.find(loc);
00319         exclusive_unit_draw_requests_t::iterator request = exclusive_unit_draw_requests_.find(loc);
00320         if (u_it != units_.end()
00321                 && (request == exclusive_unit_draw_requests_.end() || request->second == u_it->id()))
00322             u_it->redraw_unit();
00323     }
00324 
00325     halo::unrender(invalidated_);
00326 }
00327 
00328 void game_display::post_commit()
00329 {
00330     halo::render();
00331 }
00332 
00333 void game_display::draw_hex(const map_location& loc)
00334 {
00335     const bool on_map = get_map().on_board(loc);
00336     const bool is_shrouded = shrouded(loc);
00337     const bool is_fogged = fogged(loc);
00338     const int xpos = get_location_x(loc);
00339     const int ypos = get_location_y(loc);
00340 
00341     image::TYPE image_type = get_image_type(loc);
00342 
00343     display::draw_hex(loc);
00344 
00345     if(!is_shrouded) {
00346         typedef overlay_map::const_iterator Itor;
00347         std::pair<Itor,Itor> overlays = overlays_.equal_range(loc);
00348         for( ; overlays.first != overlays.second; ++overlays.first) {
00349             if ((overlays.first->second.team_name == "" ||
00350             overlays.first->second.team_name.find(teams_[playing_team()].team_name()) != std::string::npos)
00351             && !(is_fogged && !overlays.first->second.visible_in_fog))
00352             {
00353                 drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos,
00354                     image::get_image(overlays.first->second.image,image_type));
00355             }
00356         }
00357         // village-control flags.
00358         drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos, get_flag(loc));
00359     }
00360 
00361     // Draw reach_map information.
00362     // We remove the reachability mask of the unit
00363     // that we want to attack.
00364     if (!is_shrouded && !reach_map_.empty()
00365             && reach_map_.find(loc) == reach_map_.end() && loc != attack_indicator_dst_) {
00366         static const image::locator unreachable(game_config::images::unreachable);
00367         drawing_buffer_add(LAYER_REACHMAP, loc, xpos, ypos,
00368                 image::get_image(unreachable,image::SCALED_TO_HEX));
00369     }
00370 
00371     resources::whiteboard->draw_hex(loc);
00372 
00373     if (!(resources::whiteboard->is_active() && resources::whiteboard->has_temp_move()))
00374     {
00375         // Footsteps indicating a movement path
00376         const std::vector<surface>& footstepImages = footsteps_images(loc);
00377         if (footstepImages.size() != 0) {
00378             drawing_buffer_add(LAYER_FOOTSTEPS, loc, xpos, ypos, footstepImages);
00379         }
00380     }
00381     // Draw the attack direction indicator
00382     if(on_map && loc == attack_indicator_src_) {
00383         drawing_buffer_add(LAYER_ATTACK_INDICATOR, loc, xpos, ypos,
00384             image::get_image("misc/attack-indicator-src-" + attack_indicator_direction() + ".png", image::SCALED_TO_HEX));
00385     } else if (on_map && loc == attack_indicator_dst_) {
00386         drawing_buffer_add(LAYER_ATTACK_INDICATOR, loc, xpos, ypos,
00387             image::get_image("misc/attack-indicator-dst-" + attack_indicator_direction() + ".png", image::SCALED_TO_HEX));
00388     }
00389 
00390     // Linger overlay unconditionally otherwise it might give glitches
00391     // so it's drawn over the shroud and fog.
00392     if(game_mode_ != RUNNING) {
00393         static const image::locator linger(game_config::images::linger);
00394         drawing_buffer_add(LAYER_LINGER_OVERLAY, loc, xpos, ypos,
00395             image::get_image(linger, image::TOD_COLORED));
00396     }
00397 
00398     if(on_map && loc == selectedHex_ && !game_config::images::selected.empty()) {
00399         static const image::locator selected(game_config::images::selected);
00400         drawing_buffer_add(LAYER_SELECTED_HEX, loc, xpos, ypos,
00401                 image::get_image(selected, image::SCALED_TO_HEX));
00402     }
00403 
00404     // Show def% and turn to reach info
00405     if(!is_shrouded && on_map) {
00406         draw_movement_info(loc);
00407     }
00408 
00409     if(game_config::debug) {
00410         int debugH = debugHighlights_[loc];
00411         if (debugH) {
00412             std::string txt = lexical_cast<std::string>(debugH);
00413             draw_text_in_hex(loc, LAYER_MOVE_INFO, txt, 18, font::BAD_COLOR);
00414         }
00415     }
00416     //simulate_delay += 1;
00417 }
00418 
00419 const time_of_day& game_display::get_time_of_day(const map_location& loc) const
00420 {
00421     return tod_manager_.get_time_of_day(loc);
00422 }
00423 
00424 bool game_display::has_time_area() const
00425 {
00426     return tod_manager_.has_time_area();
00427 }
00428 
00429 void game_display::draw_report(const std::string &report_name)
00430 {
00431     if(!team_valid()) {
00432         return;
00433     }
00434 
00435     refresh_report(report_name, reports::generate_report(report_name));
00436 }
00437 
00438 void game_display::draw_sidebar()
00439 {
00440     wb::scoped_planned_pathfind_map future; //< Lasts for whole method.
00441 
00442     draw_report("report_clock");
00443     draw_report("report_countdown");
00444 
00445     if(teams_.empty()) {
00446         return;
00447     }
00448 
00449     if (invalidateGameStatus_)
00450     {
00451         // We display the unit the mouse is over if it is over a unit,
00452         // otherwise we display the unit that is selected.
00453         foreach (const std::string &name, reports::report_list()) {
00454             draw_report(name);
00455         }
00456         invalidateGameStatus_ = false;
00457     }
00458 }
00459 
00460 void game_display::draw_minimap_units()
00461 {
00462     double xscaling = 1.0 * minimap_location_.w / get_map().w();
00463     double yscaling = 1.0 * minimap_location_.h / get_map().h();
00464 
00465     for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u) {
00466         if (fogged(u->get_location()) ||
00467             (teams_[currentTeam_].is_enemy(u->side()) &&
00468              u->invisible(u->get_location())) ||
00469              u->get_hidden()) {
00470             continue;
00471         }
00472 
00473         int side = u->side();
00474         const SDL_Color col = team::get_minimap_color(side);
00475         const Uint32 mapped_col = SDL_MapRGB(video().getSurface()->format,col.r,col.g,col.b);
00476 
00477         double u_x = u->get_location().x * xscaling;
00478         double u_y = (u->get_location().y + (is_odd(u->get_location().x) ? 1 : -1)/4.0) * yscaling;
00479         // use 4/3 to compensate the horizontal hexes imbrication
00480         double u_w = 4.0 / 3.0 * xscaling;
00481         double u_h = yscaling;
00482 
00483         SDL_Rect r = create_rect(minimap_location_.x + round_double(u_x)
00484                 , minimap_location_.y + round_double(u_y)
00485                 , round_double(u_w)
00486                 , round_double(u_h));
00487 
00488         sdl_fill_rect(video().getSurface(), &r, mapped_col);
00489     }
00490 }
00491 
00492 void game_display::draw_bar(const std::string& image, int xpos, int ypos,
00493         const map_location& loc, size_t height, double filled,
00494         const SDL_Color& col, fixed_t alpha)
00495 {
00496 
00497     filled = std::min<double>(std::max<double>(filled,0.0),1.0);
00498     height = static_cast<size_t>(height*get_zoom_factor());
00499 #ifdef USE_TINY_GUI
00500     height /= 2;
00501 #endif
00502 
00503     surface surf(image::get_image(image,image::SCALED_TO_HEX));
00504 
00505     // We use UNSCALED because scaling (and bilinear interpolaion)
00506     // is bad for calculate_energy_bar.
00507     // But we will do a geometric scaling later.
00508     surface bar_surf(image::get_image(image));
00509     if(surf == NULL || bar_surf == NULL) {
00510         return;
00511     }
00512 
00513     // calculate_energy_bar returns incorrect results if the surface colors
00514     // have changed (for example, due to bilinear interpolaion)
00515     const SDL_Rect& unscaled_bar_loc = calculate_energy_bar(bar_surf);
00516 
00517     SDL_Rect bar_loc;
00518     if (surf->w == bar_surf->w && surf->h == bar_surf->h)
00519       bar_loc = unscaled_bar_loc;
00520     else {
00521       const fixed_t xratio = fxpdiv(surf->w,bar_surf->w);
00522       const fixed_t yratio = fxpdiv(surf->h,bar_surf->h);
00523       const SDL_Rect scaled_bar_loc = create_rect(
00524                 fxptoi(unscaled_bar_loc. x * xratio)
00525               , fxptoi(unscaled_bar_loc. y * yratio + 127)
00526               , fxptoi(unscaled_bar_loc. w * xratio + 255)
00527               , fxptoi(unscaled_bar_loc. h * yratio + 255));
00528       bar_loc = scaled_bar_loc;
00529     }
00530 
00531     if(height > bar_loc.h) {
00532         height = bar_loc.h;
00533     }
00534 
00535     //if(alpha != ftofxp(1.0)) {
00536     //  surf.assign(adjust_surface_alpha(surf,alpha));
00537     //  if(surf == NULL) {
00538     //      return;
00539     //  }
00540     //}
00541 
00542     const size_t skip_rows = bar_loc.h - height;
00543 
00544     SDL_Rect top = create_rect(0, 0, surf->w, bar_loc.y);
00545     SDL_Rect bot = create_rect(0, bar_loc.y + skip_rows, surf->w, 0);
00546     bot.h = surf->w - bot.y;
00547 
00548     drawing_buffer_add(LAYER_UNIT_BAR, loc, xpos, ypos, surf, top);
00549     drawing_buffer_add(LAYER_UNIT_BAR, loc, xpos, ypos + top.h, surf, bot);
00550 
00551     size_t unfilled = static_cast<size_t>(height * (1.0 - filled));
00552 
00553     if(unfilled < height && alpha >= ftofxp(0.3)) {
00554         const Uint8 r_alpha = std::min<unsigned>(unsigned(fxpmult(alpha,255)),255);
00555         surface filled_surf = create_compatible_surface(bar_surf, bar_loc.w, height - unfilled);
00556         SDL_Rect filled_area = create_rect(0, 0, bar_loc.w, height-unfilled);
00557         sdl_fill_rect(filled_surf,&filled_area,SDL_MapRGBA(bar_surf->format,col.r,col.g,col.b, r_alpha));
00558         drawing_buffer_add(LAYER_UNIT_BAR, loc, xpos + bar_loc.x, ypos + bar_loc.y + unfilled, filled_surf);
00559     }
00560 }
00561 
00562 void game_display::set_game_mode(const tgame_mode game_mode)
00563 {
00564     if(game_mode != game_mode_) {
00565         game_mode_ = game_mode;
00566         invalidate_all();
00567     }
00568 }
00569 
00570 void game_display::draw_movement_info(const map_location& loc)
00571 {
00572     // Search if there is a mark here
00573     pathfind::marked_route::mark_map::iterator w = route_.marks.find(loc);
00574 
00575     // Don't use empty route or the first step (the unit will be there)
00576     if(w != route_.marks.end()
00577                 && !route_.steps.empty() && route_.steps.front() != loc) {
00578         const unit_map::const_iterator un = units_.find(route_.steps.front());
00579         if(un != units_.end()) {
00580             // Display the def% of this terrain
00581             int def =  100 - un->defense_modifier(get_map().get_terrain(loc));
00582             std::stringstream def_text;
00583             def_text << def << "%";
00584 
00585             SDL_Color color = int_to_color(game_config::red_to_green(def, false));
00586 
00587             // simple mark (no turn point) use smaller font
00588             int def_font = w->second.turns > 0 ? 18 : 16;
00589             draw_text_in_hex(loc, LAYER_MOVE_INFO, def_text.str(), def_font, color);
00590 
00591             int xpos = get_location_x(loc);
00592             int ypos = get_location_y(loc);
00593 
00594             if (w->second.invisible) {
00595                 drawing_buffer_add(LAYER_MOVE_INFO, loc, xpos, ypos,
00596                     image::get_image("misc/hidden.png", image::SCALED_TO_HEX));
00597             }
00598 
00599             if (w->second.zoc) {
00600                 drawing_buffer_add(LAYER_MOVE_INFO, loc, xpos, ypos,
00601                     image::get_image("misc/zoc.png", image::SCALED_TO_HEX));
00602             }
00603 
00604             if (w->second.capture) {
00605                 drawing_buffer_add(LAYER_MOVE_INFO, loc, xpos, ypos,
00606                     image::get_image("misc/capture.png", image::SCALED_TO_HEX));
00607             }
00608 
00609             if (w->second.pass_here) {
00610                 drawing_buffer_add(LAYER_MOVE_INFO, loc, xpos, ypos,
00611                     image::get_image("misc/waypoint.png", image::SCALED_TO_HEX));
00612             }
00613 
00614             //we display turn info only if different from a simple last "1"
00615             if (w->second.turns > 1 || (w->second.turns == 1 && loc != route_.steps.back())) {
00616                 std::stringstream turns_text;
00617                 turns_text << w->second.turns;
00618                 draw_text_in_hex(loc, LAYER_MOVE_INFO, turns_text.str(), 17, font::NORMAL_COLOR, 0.5,0.8);
00619             }
00620 
00621             // The hex is full now, so skip the "show enemy moves"
00622             return;
00623         }
00624     }
00625     // When out-of-turn, it's still interesting to check out the terrain defs of the selected unit
00626     else if (selectedHex_.valid() && loc == mouseoverHex_)
00627     {
00628         const unit_map::const_iterator selectedUnit = units_.find(selectedHex_);
00629         const unit_map::const_iterator mouseoveredUnit = units_.find(mouseoverHex_);
00630         if(selectedUnit != units_.end() && mouseoveredUnit == units_.end()) {
00631             // Display the def% of this terrain
00632             int def =  100 - selectedUnit->defense_modifier(get_map().get_terrain(loc));
00633             std::stringstream def_text;
00634             def_text << def << "%";
00635 
00636             SDL_Color color = int_to_color(game_config::red_to_green(def, false));
00637 
00638             // use small font
00639             int def_font = 16;
00640             draw_text_in_hex(loc, LAYER_MOVE_INFO, def_text.str(), def_font, color);
00641         }
00642     }
00643 
00644     if (!reach_map_.empty()) {
00645         reach_map::iterator reach = reach_map_.find(loc);
00646         if (reach != reach_map_.end() && reach->second > 1) {
00647             const std::string num = lexical_cast<std::string>(reach->second);
00648             draw_text_in_hex(loc, LAYER_MOVE_INFO, num, 16, font::YELLOW_COLOR);
00649         }
00650     }
00651 }
00652 
00653 std::vector<surface> game_display::footsteps_images(const map_location& loc)
00654 {
00655     std::vector<surface> res;
00656 
00657     if (route_.steps.size() < 2) {
00658         return res; // no real "route"
00659     }
00660 
00661     std::vector<map_location>::const_iterator i =
00662              std::find(route_.steps.begin(),route_.steps.end(),loc);
00663 
00664     if( i == route_.steps.end()) {
00665         return res; // not on the route
00666     }
00667 
00668     // Check which footsteps images of game_config we will use
00669     int move_cost = 1;
00670     const unit_map::const_iterator u = units_.find(route_.steps.front());
00671     if(u != units_.end()) {
00672         move_cost = u->movement_cost(get_map().get_terrain(loc));
00673     }
00674     int image_number = std::min<int>(move_cost, game_config::foot_speed_prefix.size());
00675     if (image_number < 1) {
00676         return res; // Invalid movement cost or no images
00677     }
00678     const std::string foot_speed_prefix = game_config::foot_speed_prefix[image_number-1];
00679 
00680     surface teleport = NULL;
00681 
00682     // We draw 2 half-hex (with possibly different directions),
00683     // but skip the first for the first step.
00684     const int first_half = (i == route_.steps.begin()) ? 1 : 0;
00685     // and the second for the last step
00686     const int second_half = (i+1 == route_.steps.end()) ? 0 : 1;
00687 
00688     for (int h = first_half; h <= second_half; ++h) {
00689         const std::string sense( h==0 ? "-in" : "-out" );
00690 
00691         if (!tiles_adjacent(*(i+(h-1)), *(i+h))) {
00692             std::string teleport_image =
00693             h==0 ? game_config::foot_teleport_enter : game_config::foot_teleport_exit;
00694             teleport = image::get_image(teleport_image, image::SCALED_TO_HEX);
00695             continue;
00696         }
00697 
00698         // In function of the half, use the incoming or outgoing direction
00699         map_location::DIRECTION dir = (i+(h-1))->get_relative_dir(*(i+h));
00700 
00701         std::string rotate;
00702         if (dir > map_location::SOUTH_EAST) {
00703             // No image, take the opposite direction and do a 180 rotation
00704             dir = i->get_opposite_dir(dir);
00705             rotate = "~FL(horiz)~FL(vert)";
00706         }
00707 
00708         const std::string image = foot_speed_prefix
00709             + sense + "-" + i->write_direction(dir)
00710             + ".png" + rotate;
00711 
00712         res.push_back(image::get_image(image, image::SCALED_TO_HEX));
00713     }
00714 
00715     // we draw teleport image (if any) in last
00716     if (teleport != NULL) res.push_back(teleport);
00717 
00718     return res;
00719 }
00720 
00721 surface game_display::get_flag(const map_location& loc)
00722 {
00723     t_translation::t_terrain terrain = get_map().get_terrain(loc);
00724 
00725     if(!get_map().is_village(terrain)) {
00726         return surface(NULL);
00727     }
00728 
00729     for(size_t i = 0; i != teams_.size(); ++i) {
00730         if(teams_[i].owns_village(loc) &&
00731           (!fogged(loc) || !teams_[currentTeam_].is_enemy(i+1)))
00732         {
00733             flags_[i].update_last_draw_time();
00734             const image::locator &image_flag = animate_map_ ?
00735                 flags_[i].get_current_frame() : flags_[i].get_first_frame();
00736             return image::get_image(image_flag, image::TOD_COLORED);
00737         }
00738     }
00739 
00740     return surface(NULL);
00741 }
00742 
00743 void game_display::highlight_reach(const pathfind::paths &paths_list)
00744 {
00745     unhighlight_reach();
00746     highlight_another_reach(paths_list);
00747 }
00748 
00749 void game_display::highlight_another_reach(const pathfind::paths &paths_list)
00750 {
00751     // Fold endpoints of routes into reachability map.
00752     foreach (const pathfind::paths::step &dest, paths_list.destinations) {
00753         reach_map_[dest.curr]++;
00754     }
00755     reach_map_changed_ = true;
00756 }
00757 
00758 void game_display::unhighlight_reach()
00759 {
00760     reach_map_ = reach_map();
00761     reach_map_changed_ = true;
00762 }
00763 
00764 void game_display::process_reachmap_changes()
00765 {
00766     if (!reach_map_changed_) return;
00767     if (reach_map_.empty() != reach_map_old_.empty()) {
00768         // Invalidate everything except the non-darkened tiles
00769         reach_map &full = reach_map_.empty() ? reach_map_old_ : reach_map_;
00770 
00771         rect_of_hexes hexes = get_visible_hexes();
00772         rect_of_hexes::iterator i = hexes.begin(), end = hexes.end();
00773         for (;i != end; ++i) {
00774             reach_map::iterator reach = full.find(*i);
00775             if (reach == full.end()) {
00776                 // Location needs to be darkened or brightened
00777                 invalidate(*i);
00778             } else if (reach->second != 1) {
00779                 // Number needs to be displayed or cleared
00780                 invalidate(*i);
00781             }
00782         }
00783     } else if (!reach_map_.empty()) {
00784         // Invalidate only changes
00785         reach_map::iterator reach, reach_old;
00786         for (reach = reach_map_.begin(); reach != reach_map_.end(); ++reach) {
00787             reach_old = reach_map_old_.find(reach->first);
00788             if (reach_old == reach_map_old_.end()) {
00789                 invalidate(reach->first);
00790             } else {
00791                 if (reach_old->second != reach->second) {
00792                     invalidate(reach->first);
00793                 }
00794                 reach_map_old_.erase(reach_old);
00795             }
00796         }
00797         for (reach_old = reach_map_old_.begin(); reach_old != reach_map_old_.end(); ++reach_old) {
00798             invalidate(reach_old->first);
00799         }
00800     }
00801     reach_map_old_ = reach_map_;
00802     reach_map_changed_ = false;
00803 }
00804 
00805 void game_display::invalidate_route()
00806 {
00807     for(std::vector<map_location>::const_iterator i = route_.steps.begin();
00808         i != route_.steps.end(); ++i) {
00809         invalidate(*i);
00810     }
00811 }
00812 
00813 void game_display::set_route(const pathfind::marked_route *route)
00814 {
00815     invalidate_route();
00816 
00817     if(route != NULL) {
00818         route_ = *route;
00819     } else {
00820         route_.steps.clear();
00821         route_.marks.clear();
00822     }
00823 
00824     invalidate_route();
00825 }
00826 
00827 void game_display::float_label(const map_location& loc, const std::string& text,
00828                           int red, int green, int blue)
00829 {
00830     if(preferences::show_floating_labels() == false || fogged(loc)) {
00831         return;
00832     }
00833 
00834     font::floating_label flabel(text);
00835     flabel.set_font_size(font::SIZE_XLARGE);
00836     const SDL_Color color = create_color(red, green, blue);
00837     flabel.set_color(color);
00838     flabel.set_position(get_location_x(loc)+zoom_/2, get_location_y(loc));
00839     flabel.set_move(0, -2 * turbo_speed());
00840     flabel.set_lifetime(60/turbo_speed());
00841     flabel.set_scroll_mode(font::ANCHOR_LABEL_MAP);
00842 
00843     font::add_floating_label(flabel);
00844 }
00845 
00846 struct is_energy_color {
00847     bool operator()(Uint32 color) const { return (color&0xFF000000) > 0x10000000 &&
00848                                                   (color&0x00FF0000) < 0x00100000 &&
00849                                                   (color&0x0000FF00) < 0x00001000 &&
00850                                                   (color&0x000000FF) < 0x00000010; }
00851 };
00852 
00853 const SDL_Rect& game_display::calculate_energy_bar(surface surf)
00854 {
00855     const std::map<surface,SDL_Rect>::const_iterator i = energy_bar_rects_.find(surf);
00856     if(i != energy_bar_rects_.end()) {
00857         return i->second;
00858     }
00859 
00860     int first_row = -1, last_row = -1, first_col = -1, last_col = -1;
00861 
00862     surface image(make_neutral_surface(surf));
00863 
00864     const_surface_lock image_lock(image);
00865     const Uint32* const begin = image_lock.pixels();
00866 
00867     for(int y = 0; y != image->h; ++y) {
00868         const Uint32* const i1 = begin + image->w*y;
00869         const Uint32* const i2 = i1 + image->w;
00870         const Uint32* const itor = std::find_if(i1,i2,is_energy_color());
00871         const int count = std::count_if(itor,i2,is_energy_color());
00872 
00873         if(itor != i2) {
00874             if(first_row == -1) {
00875                 first_row = y;
00876             }
00877 
00878             first_col = itor - i1;
00879             last_col = first_col + count;
00880             last_row = y;
00881         }
00882     }
00883 
00884     const SDL_Rect res = create_rect(first_col
00885             , first_row
00886             , last_col-first_col
00887             , last_row+1-first_row);
00888     energy_bar_rects_.insert(std::pair<surface,SDL_Rect>(surf,res));
00889     return calculate_energy_bar(surf);
00890 }
00891 
00892 void game_display::invalidate_animations_location(const map_location& loc) {
00893     if (get_map().is_village(loc)) {
00894         const int owner = player_teams::village_owner(loc);
00895         if (owner >= 0 && flags_[owner].need_update()
00896         && (!fogged(loc) || !teams_[currentTeam_].is_enemy(owner+1))) {
00897             invalidate(loc);
00898         }
00899     }
00900 }
00901 
00902 void game_display::invalidate_animations()
00903 {
00904     display::invalidate_animations();
00905     foreach (unit& u, units_) {
00906         u.refresh();
00907     }
00908     foreach(unit* temp_unit, temp_units_) {
00909         temp_unit->refresh();
00910     }
00911     bool new_inval = true;
00912     while(new_inval) {
00913         new_inval = false;
00914         foreach (unit& u, units_) {
00915             new_inval |= u.invalidate(u.get_location());
00916         }
00917         foreach(unit* temp_unit, temp_units_) {
00918             new_inval |= temp_unit->invalidate(temp_unit->get_location());
00919         }
00920     }
00921 }
00922 
00923 int& game_display::debug_highlight(const map_location& loc)
00924 {
00925     assert(game_config::debug);
00926     return debugHighlights_[loc];
00927 }
00928 
00929 void game_display::place_temporary_unit(unit *u)
00930 {
00931     if(std::find(temp_units_.begin(),temp_units_.end(), u) != temp_units_.end()) {
00932         ERR_NG << "In game_display::place_temporary_unit: attempt to add duplicate fake unit." << std::endl;
00933     } else {
00934         temp_units_.push_back(u);
00935         invalidate(u->get_location());
00936     }
00937 }
00938 
00939 int game_display::remove_temporary_unit(unit *u)
00940 {
00941     int removed = 0;
00942     std::deque<unit*>::iterator it =
00943             std::remove(temp_units_.begin(), temp_units_.end(), u);
00944     if (it != temp_units_.end()) {
00945         removed = std::distance(it, temp_units_.end());
00946         //std::remove doesn't remove anything without using erase afterwards.
00947         temp_units_.erase(it, temp_units_.end());
00948         invalidate(u->get_location());
00949         // Redraw with no location to get rid of haloes
00950         u->clear_haloes();
00951     }
00952     if (removed > 1) {
00953         ERR_NG << "Error: duplicate temp unit found in game_display::remove_temporary_unit" << std::endl;
00954     }
00955     return removed;
00956 }
00957 
00958 bool game_display::add_exclusive_draw(const map_location& loc, unit& unit)
00959 {
00960     if (loc.valid() && exclusive_unit_draw_requests_.find(loc) == exclusive_unit_draw_requests_.end())
00961     {
00962         exclusive_unit_draw_requests_[loc] = unit.id();
00963         return true;
00964     }
00965     else
00966     {
00967         return false;
00968     }
00969 }
00970 
00971 std::string game_display::remove_exclusive_draw(const map_location& loc)
00972 {
00973     std::string id = "";
00974     if(loc.valid())
00975     {
00976         id = exclusive_unit_draw_requests_[loc];
00977         //id will be set to the default "" string by the [] operator if the map doesn't have anything for that loc.
00978         exclusive_unit_draw_requests_.erase(loc);
00979     }
00980     return id;
00981 }
00982 
00983 void game_display::set_attack_indicator(const map_location& src, const map_location& dst)
00984 {
00985     if (attack_indicator_src_ != src || attack_indicator_dst_ != dst) {
00986         invalidate(attack_indicator_src_);
00987         invalidate(attack_indicator_dst_);
00988 
00989         attack_indicator_src_ = src;
00990         attack_indicator_dst_ = dst;
00991 
00992         invalidate(attack_indicator_src_);
00993         invalidate(attack_indicator_dst_);
00994     }
00995 }
00996 
00997 void game_display::clear_attack_indicator()
00998 {
00999     set_attack_indicator(map_location::null_location, map_location::null_location);
01000 }
01001 
01002 void game_display::add_overlay(const map_location& loc, const std::string& img, const std::string& halo,const std::string& team_name, bool visible_under_fog)
01003 {
01004     const int halo_handle = halo::add(get_location_x(loc) + hex_size() / 2,
01005             get_location_y(loc) + hex_size() / 2, halo, loc);
01006 
01007     const overlay item(img, halo, halo_handle, team_name, visible_under_fog);
01008     overlays_.insert(overlay_map::value_type(loc,item));
01009 }
01010 
01011 void game_display::remove_overlay(const map_location& loc)
01012 {
01013     typedef overlay_map::const_iterator Itor;
01014     std::pair<Itor,Itor> itors = overlays_.equal_range(loc);
01015     while(itors.first != itors.second) {
01016         halo::remove(itors.first->second.halo_handle);
01017         ++itors.first;
01018     }
01019 
01020     overlays_.erase(loc);
01021 }
01022 
01023 void game_display::remove_single_overlay(const map_location& loc, const std::string& toDelete)
01024 {
01025     //Iterate through the values with key of loc
01026     typedef overlay_map::iterator Itor;
01027     overlay_map::iterator iteratorCopy;
01028     std::pair<Itor,Itor> itors = overlays_.equal_range(loc);
01029     while(itors.first != itors.second) {
01030         //If image or halo of overlay struct matches toDelete, remove the overlay
01031         if(itors.first->second.image == toDelete || itors.first->second.halo == toDelete) {
01032             iteratorCopy = itors.first;
01033             ++itors.first;
01034             halo::remove(iteratorCopy->second.halo_handle);
01035             overlays_.erase(iteratorCopy);
01036         }
01037         else {
01038             ++itors.first;
01039         }
01040     }
01041 }
01042 
01043 void game_display::parse_team_overlays()
01044 {
01045     const team& curr_team = teams_[playing_team()];
01046     const team& prev_team = teams_[playing_team()-1 < teams_.size() ? playing_team()-1 : teams_.size()-1];
01047     foreach (const game_display::overlay_map::value_type i, overlays_) {
01048         const overlay& ov = i.second;
01049         if (!ov.team_name.empty() &&
01050             ((ov.team_name.find(curr_team.team_name()) + 1) != 0) !=
01051             ((ov.team_name.find(prev_team.team_name()) + 1) != 0))
01052         {
01053             invalidate(i.first);
01054         }
01055     }
01056 }
01057 
01058 std::string game_display::current_team_name() const
01059 {
01060     if (team_valid())
01061     {
01062         return teams_[currentTeam_].team_name();
01063     }
01064     return std::string();
01065 }
01066 
01067 #ifdef HAVE_LIBDBUS
01068 /** Use KDE 4 notifications. */
01069 static bool kde_style = false;
01070 
01071 struct wnotify
01072 {
01073     uint32_t id;
01074     std::string owner;
01075     std::string message;
01076 };
01077 
01078 static std::list<wnotify> notifications;
01079 
01080 static DBusHandlerResult filter_dbus_signal(DBusConnection *, DBusMessage *buf, void *)
01081 {
01082     if (!dbus_message_is_signal(buf, "org.freedesktop.Notifications", "NotificationClosed")) {
01083         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
01084     }
01085 
01086     uint32_t id;
01087     dbus_message_get_args(buf, NULL,
01088         DBUS_TYPE_UINT32, &id,
01089         DBUS_TYPE_INVALID);
01090 
01091     std::list<wnotify>::iterator i = notifications.begin(),
01092         i_end = notifications.end();
01093     while (i != i_end && i->id != id) ++i;
01094     if (i != i_end)
01095         notifications.erase(i);
01096 
01097     return DBUS_HANDLER_RESULT_HANDLED;
01098 }
01099 
01100 static DBusConnection *get_dbus_connection()
01101 {
01102     static bool initted = false;
01103     static DBusConnection *connection = NULL;
01104     if (!initted)
01105     {
01106         initted = true;
01107         if (preferences::get("disable_notifications", false)) {
01108             return NULL;
01109         }
01110         if (getenv("KDE_SESSION_VERSION")) {
01111             // This variable is defined for KDE 4 only.
01112             kde_style = true;
01113         }
01114         DBusError err;
01115         dbus_error_init(&err);
01116         connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
01117         if (!connection) {
01118             ERR_DP << "Failed to open DBus session: " << err.message << '\n';
01119             dbus_error_free(&err);
01120             return NULL;
01121         }
01122         dbus_connection_add_filter(connection, filter_dbus_signal, NULL, NULL);
01123     }
01124     if (connection) {
01125         dbus_connection_read_write(connection, 0);
01126         while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) {}
01127     }
01128     return connection;
01129 }
01130 
01131 static uint32_t send_dbus_notification(DBusConnection *connection, uint32_t replaces_id,
01132     const std::string &owner, const std::string &message)
01133 {
01134     DBusMessage *buf = dbus_message_new_method_call(
01135         kde_style ? "org.kde.VisualNotifications" : "org.freedesktop.Notifications",
01136         kde_style ? "/VisualNotifications" : "/org/freedesktop/Notifications",
01137         kde_style ? "org.kde.VisualNotifications" : "org.freedesktop.Notifications",
01138         "Notify");
01139     const char *app_name = "Battle for Wesnoth";
01140     dbus_message_append_args(buf,
01141         DBUS_TYPE_STRING, &app_name,
01142         DBUS_TYPE_UINT32, &replaces_id,
01143         DBUS_TYPE_INVALID);
01144     if (kde_style) {
01145         const char *event_id = "";
01146         dbus_message_append_args(buf,
01147             DBUS_TYPE_STRING, &event_id,
01148             DBUS_TYPE_INVALID);
01149     }
01150     std::string app_icon_ = game_config::path + "/images/wesnoth-icon-small.png";
01151     const char *app_icon = app_icon_.c_str();
01152     const char *summary = owner.c_str();
01153     const char *body = message.c_str();
01154     const char **actions = NULL;
01155     dbus_message_append_args(buf,
01156         DBUS_TYPE_STRING, &app_icon,
01157         DBUS_TYPE_STRING, &summary,
01158         DBUS_TYPE_STRING, &body,
01159         DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &actions, 0,
01160         DBUS_TYPE_INVALID);
01161     DBusMessageIter iter, hints;
01162     dbus_message_iter_init_append(buf, &iter);
01163     dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &hints);
01164     dbus_message_iter_close_container(&iter, &hints);
01165     int expire_timeout = kde_style ? 5000 : -1;
01166     dbus_message_append_args(buf,
01167         DBUS_TYPE_INT32, &expire_timeout,
01168         DBUS_TYPE_INVALID);
01169     DBusError err;
01170     dbus_error_init(&err);
01171     DBusMessage *ret = dbus_connection_send_with_reply_and_block(connection, buf, 1000, &err);
01172     dbus_message_unref(buf);
01173     if (!ret) {
01174         ERR_DP << "Failed to send visual notification: " << err.message << '\n';
01175         dbus_error_free(&err);
01176         if (kde_style) {
01177             ERR_DP << " Retrying with the freedesktop protocol.\n";
01178             kde_style = false;
01179             return send_dbus_notification(connection, replaces_id, owner, message);
01180         }
01181         return 0;
01182     }
01183     uint32_t id;
01184     dbus_message_get_args(ret, NULL,
01185         DBUS_TYPE_UINT32, &id,
01186         DBUS_TYPE_INVALID);
01187     dbus_message_unref(ret);
01188     // TODO: remove once closing signals for KDE are handled in filter_dbus_signal.
01189     if (kde_style) return 0;
01190     return id;
01191 }
01192 #endif
01193 
01194 #if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL)
01195 void game_display::send_notification(const std::string& owner, const std::string& message)
01196 #else
01197 void game_display::send_notification(const std::string& /*owner*/, const std::string& /*message*/)
01198 #endif
01199 {
01200 #if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL)
01201     Uint8 app_state = SDL_GetAppState();
01202 
01203     // Do not show notifications when the window is visible...
01204     if ((app_state & SDL_APPACTIVE) != 0)
01205     {
01206         // ... and it has a focus.
01207         if ((app_state & (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS)) != 0) {
01208             return;
01209         }
01210     }
01211 #endif
01212 
01213 #ifdef HAVE_LIBDBUS
01214     DBusConnection *connection = get_dbus_connection();
01215     if (!connection) return;
01216 
01217     std::list<wnotify>::iterator i = notifications.begin(),
01218         i_end = notifications.end();
01219     while (i != i_end && i->owner != owner) ++i;
01220 
01221     if (i != i_end) {
01222         i->message += "\n";
01223         i->message += message;
01224         send_dbus_notification(connection, i->id, owner, i->message);
01225         return;
01226     }
01227 
01228     uint32_t id = send_dbus_notification(connection, 0, owner, message);
01229     if (!id) return;
01230     wnotify visual;
01231     visual.id = id;
01232     visual.owner = owner;
01233     visual.message = message;
01234     notifications.push_back(visual);
01235 #endif
01236 
01237 #ifdef HAVE_GROWL
01238     CFStringRef app_name = CFStringCreateWithCString(NULL, "Wesnoth", kCFStringEncodingUTF8);
01239     CFStringRef cf_owner = CFStringCreateWithCString(NULL, owner.c_str(), kCFStringEncodingUTF8);
01240     CFStringRef cf_message = CFStringCreateWithCString(NULL, message.c_str(), kCFStringEncodingUTF8);
01241     //Should be changed as soon as there are more than 2 types of notifications
01242     CFStringRef cf_note_name = CFStringCreateWithCString(NULL, owner == "Turn changed" ? "Turn changed" : "Chat message", kCFStringEncodingUTF8);
01243 
01244     growl_obj.applicationName = app_name;
01245     growl_obj.registrationDictionary = NULL;
01246     growl_obj.applicationIconData = NULL;
01247     growl_obj.growlIsReady = NULL;
01248     growl_obj.growlNotificationWasClicked = NULL;
01249     growl_obj.growlNotificationTimedOut = NULL;
01250 
01251     Growl_SetDelegate(&growl_obj);
01252     Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext(cf_owner, cf_message, cf_note_name, NULL, NULL, NULL, NULL);
01253 
01254     CFRelease(app_name);
01255     CFRelease(cf_owner);
01256     CFRelease(cf_message);
01257     CFRelease(cf_note_name);
01258 #endif
01259 }
01260 
01261 void game_display::set_team(size_t teamindex, bool show_everything)
01262 {
01263     assert(teamindex < teams_.size());
01264     currentTeam_ = teamindex;
01265     if (!show_everything)
01266     {
01267         labels().set_team(&teams_[teamindex]);
01268         viewpoint_ = &teams_[teamindex];
01269     }
01270     else
01271     {
01272         labels().set_team(0);
01273         viewpoint_ = NULL;
01274     }
01275     labels().recalculate_labels();
01276 }
01277 
01278 void game_display::set_playing_team(size_t teamindex)
01279 {
01280     assert(teamindex < teams_.size());
01281     activeTeam_ = teamindex;
01282     invalidate_game_status();
01283 }
01284 
01285 void game_display::begin_game()
01286 {
01287     in_game_ = true;
01288     create_buttons();
01289     invalidate_all();
01290 }
01291 
01292 namespace {
01293     const int chat_message_border = 5;
01294     const int chat_message_x = 10;
01295     const int chat_message_y = 10;
01296     const SDL_Color chat_message_color = {255,255,255,255};
01297     const SDL_Color chat_message_bg     = {0,0,0,140};
01298 }
01299 
01300 void game_display::add_chat_message(const time_t& time, const std::string& speaker,
01301         int side, const std::string& message, events::chat_handler::MESSAGE_TYPE type,
01302         bool bell)
01303 {
01304     const bool whisper = speaker.find("whisper: ") == 0;
01305     std::string sender = speaker;
01306     if (whisper) {
01307         sender.assign(speaker, 9, speaker.size());
01308     }
01309     if (!preferences::parse_should_show_lobby_join(sender, message)) return;
01310     if (preferences::is_ignored(sender)) return;
01311 
01312     preferences::parse_admin_authentication(sender, message);
01313 
01314     if (bell) {
01315         if ((type == events::chat_handler::MESSAGE_PRIVATE && (!is_observer() || whisper))
01316             || utils::word_match(message, preferences::login())) {
01317             sound::play_UI_sound(game_config::sounds::receive_message_highlight);
01318         } else if (preferences::is_friend(sender)) {
01319             sound::play_UI_sound(game_config::sounds::receive_message_friend);
01320         } else if (sender == "server") {
01321             sound::play_UI_sound(game_config::sounds::receive_message_server);
01322         } else {
01323             sound::play_UI_sound(game_config::sounds::receive_message);
01324         }
01325     }
01326 
01327     bool action = false;
01328 
01329     std::string msg;
01330 
01331     if (message.find("/me ") == 0) {
01332         msg.assign(message, 4, message.size());
01333         action = true;
01334     } else {
01335         msg += message;
01336     }
01337 
01338     try {
01339         // We've had a joker who send an invalid utf-8 message to crash clients
01340         // so now catch the exception and ignore the message.
01341         msg = font::word_wrap_text(msg,font::SIZE_SMALL,map_outside_area().w*3/4);
01342     } catch (utils::invalid_utf8_exception&) {
01343         ERR_NG << "Invalid utf-8 found, chat message is ignored.\n";
01344         return;
01345     }
01346 
01347     int ypos = chat_message_x;
01348     for(std::vector<chat_message>::const_iterator m = chat_messages_.begin(); m != chat_messages_.end(); ++m) {
01349         ypos += std::max(font::get_floating_label_rect(m->handle).h,
01350             font::get_floating_label_rect(m->speaker_handle).h);
01351     }
01352     SDL_Color speaker_color = {255,255,255,255};
01353     if(side >= 1) {
01354         speaker_color = int_to_color(team::get_side_color_range(side).mid());
01355     }
01356 
01357     SDL_Color message_color = chat_message_color;
01358     std::stringstream str;
01359     std::stringstream message_str;
01360 
01361     if(type ==  events::chat_handler::MESSAGE_PUBLIC) {
01362         if(action) {
01363             str << "<" << speaker << " " << msg << ">";
01364             message_color = speaker_color;
01365             message_str << " ";
01366         } else {
01367             if (!speaker.empty())
01368                 str << "<" << speaker << ">";
01369             message_str << msg;
01370         }
01371     } else {
01372         if(action) {
01373             str << "*" << speaker << " " << msg << "*";
01374             message_color = speaker_color;
01375             message_str << " ";
01376         } else {
01377             if (!speaker.empty())
01378                 str << "*" << speaker << "*";
01379             message_str << msg;
01380         }
01381     }
01382 
01383     // Prepend message with timestamp.
01384     std::stringstream message_complete;
01385     message_complete << preferences::get_chat_timestamp(time) << str.str();
01386 
01387     const SDL_Rect rect = map_outside_area();
01388 
01389     font::floating_label spk_flabel(message_complete.str());
01390     spk_flabel.set_font_size(font::SIZE_SMALL);
01391     spk_flabel.set_color(speaker_color);
01392     spk_flabel.set_position(rect.x + chat_message_x, rect.y + ypos);
01393     spk_flabel.set_clip_rect(rect);
01394     spk_flabel.set_alignment(font::LEFT_ALIGN);
01395     spk_flabel.set_bg_color(chat_message_bg);
01396     spk_flabel.set_border_size(chat_message_border);
01397     spk_flabel.use_markup(false);
01398 
01399     int speaker_handle = font::add_floating_label(spk_flabel);
01400 
01401     font::floating_label msg_flabel(message_str.str());
01402     msg_flabel.set_font_size(font::SIZE_SMALL);
01403     msg_flabel.set_color(message_color);
01404     msg_flabel.set_position(rect.x + chat_message_x + font::get_floating_label_rect(speaker_handle).w,
01405     rect.y + ypos);
01406     msg_flabel.set_clip_rect(rect);
01407     msg_flabel.set_alignment(font::LEFT_ALIGN);
01408     msg_flabel.set_bg_color(chat_message_bg);
01409     msg_flabel.set_border_size(chat_message_border);
01410     msg_flabel.use_markup(false);
01411 
01412     int message_handle = font::add_floating_label(msg_flabel);
01413 
01414     // Send system notification if appropriate.
01415     send_notification(speaker, message);
01416 
01417     chat_messages_.push_back(chat_message(speaker_handle,message_handle));
01418 
01419     prune_chat_messages();
01420 }
01421 
01422 void game_display::prune_chat_messages(bool remove_all)
01423 {
01424     unsigned message_ttl = remove_all ? 0 : 1200000;
01425     unsigned max_chat_messages = preferences::chat_lines();
01426     int movement = 0;
01427 
01428     while (!chat_messages_.empty() &&
01429            (chat_messages_.front().created_at + message_ttl < SDL_GetTicks() ||
01430             chat_messages_.size() > max_chat_messages))
01431     {
01432         const chat_message &old = chat_messages_.front();
01433         movement += font::get_floating_label_rect(old.handle).h;
01434         font::remove_floating_label(old.speaker_handle);
01435         font::remove_floating_label(old.handle);
01436         chat_messages_.erase(chat_messages_.begin());
01437     }
01438 
01439     foreach (const chat_message &cm, chat_messages_) {
01440         font::move_floating_label(cm.speaker_handle, 0, - movement);
01441         font::move_floating_label(cm.handle, 0, - movement);
01442     }
01443 }
01444 
01445 game_display *game_display::singleton_ = NULL;
01446 

Generated by doxygen 1.5.6 on Thu Feb 10 01:01:21 2011 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs