game_display.cpp

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

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs