game_display.cpp

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

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:52 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs