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