00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "global.hpp"
00019
00020 #include "actions.hpp"
00021 #include "cursor.hpp"
00022 #include "display.hpp"
00023 #include "events.hpp"
00024 #include "font.hpp"
00025 #include "game_config.hpp"
00026 #include "gettext.hpp"
00027 #include "hotkeys.hpp"
00028 #include "language.hpp"
00029 #include "log.hpp"
00030 #include "marked-up_text.hpp"
00031 #include "minimap.hpp"
00032 #include "pathfind.hpp"
00033 #include "preferences.hpp"
00034 #include "sdl_utils.hpp"
00035 #include "theme.hpp"
00036 #include "tooltips.hpp"
00037 #include "util.hpp"
00038
00039 #include "SDL_image.h"
00040
00041 #include <algorithm>
00042 #include <cassert>
00043 #ifdef __SUNPRO_CC
00044
00045 #include <math.h>
00046 #endif
00047 #include <cmath>
00048 #include <iostream>
00049 #include <sstream>
00050
00051 #define ERR_DP LOG_STREAM(err, display)
00052 #define INFO_DP LOG_STREAM(info, display)
00053 #define DBG_DP LOG_STREAM(debug, display)
00054
00055 namespace {
00056 #ifdef USE_TINY_GUI
00057 const int DefaultZoom = 36;
00058 const int SmallZoom = 16;
00059 #else
00060 const int DefaultZoom = 72;
00061 const int SmallZoom = 36;
00062 #endif
00063
00064 const int MinZoom = 4;
00065 const int MaxZoom = 200;
00066 size_t sunset_delay = 0;
00067 size_t sunset_timer = 0;
00068
00069 bool benchmark = false;
00070 }
00071
00072 display::display(CVideo& video, const gamemap& map, const config& theme_cfg, const config& cfg, const config& level) :
00073 screen_(video),
00074 map_(map),
00075 viewpoint_(NULL),
00076 xpos_(0),
00077 ypos_(0),
00078 theme_(theme_cfg,
00079 screen_area()),
00080 zoom_(DefaultZoom),
00081 last_zoom_(SmallZoom),
00082 builder_(cfg, level, map, theme_.border().tile_image),
00083 minimap_(NULL),
00084 minimap_location_(empty_rect),
00085 redrawMinimap_(false),
00086 redraw_background_(true),
00087 invalidateAll_(true),
00088 grid_(false),
00089 diagnostic_label_(0),
00090 panelsDrawn_(false),
00091 turbo_speed_(2),
00092 turbo_(false),
00093 invalidateGameStatus_(true),
00094 map_labels_(*this,map, 0),
00095 _scroll_event("scrolled"),
00096 nextDraw_(0),
00097 report_(),
00098 buttons_(),
00099 invalidated_(),
00100 hex_overlay_(),
00101 selected_hex_overlay_(0),
00102 mouseover_hex_overlay_(0),
00103 selectedHex_(),
00104 mouseoverHex_(),
00105 highlighted_locations_(),
00106 keys_(),
00107 #if TDRAWING_BUFFER_USES_VECTOR
00108 drawing_buffer_(LAYER_LAST_LAYER),
00109 #else
00110 drawing_buffer_(),
00111 #endif
00112 map_screenshot_(false),
00113 fps_handle_(0),
00114 idle_anim_(preferences::idle_anim()),
00115 idle_anim_rate_(1.0),
00116 map_screenshot_surf_(NULL)
00117 {
00118 if(non_interactive()) {
00119 screen_.lock_updates(true);
00120 }
00121
00122 set_idle_anim_rate(preferences::idle_anim_rate());
00123
00124 std::fill(reportRects_,reportRects_+reports::NUM_REPORTS,empty_rect);
00125
00126 image::set_zoom(zoom_);
00127 }
00128
00129 display::~display()
00130 {
00131 }
00132
00133 const SDL_Rect& display::max_map_area() const
00134 {
00135 static SDL_Rect max_area = {0, 0, 0, 0};
00136
00137
00138
00139
00140
00141
00142
00143
00144 max_area.w = static_cast<int>((map_.w() + 2 * theme_.border().size + 1.0/3.0) * hex_width());
00145 max_area.h = static_cast<int>((map_.h() + 2 * theme_.border().size + 0.5) * hex_size());
00146
00147 return max_area;
00148 }
00149
00150 const SDL_Rect& display::map_area() const
00151 {
00152 static SDL_Rect max_area;
00153 max_area = max_map_area();
00154
00155
00156 if (map_screenshot_) {
00157 return max_area;
00158 }
00159
00160 static SDL_Rect res;
00161 res = map_outside_area();
00162
00163 if(max_area.w < res.w) {
00164
00165 res.x += (res.w - max_area.w)/2;
00166 res.w = max_area.w;
00167 }
00168
00169 if(max_area.h < res.h) {
00170
00171 res.y += (res.h - max_area.h)/2;
00172 res.h = max_area.h;
00173 }
00174
00175 return res;
00176 }
00177
00178 bool display::outside_area(const SDL_Rect& area, const int x, const int y) const
00179 {
00180 const int x_thresh = hex_size();
00181 const int y_thresh = hex_size();
00182 return (x < area.x || x > area.x + area.w - x_thresh ||
00183 y < area.y || y > area.y + area.h - y_thresh);
00184 }
00185
00186
00187 const gamemap::location display::hex_clicked_on(int xclick, int yclick,
00188 gamemap::location::DIRECTION* nearest_hex,
00189 gamemap::location::DIRECTION* second_nearest_hex) const
00190 {
00191 const SDL_Rect& rect = map_area();
00192 if(point_in_rect(xclick,yclick,rect) == false) {
00193 return gamemap::location();
00194 }
00195
00196 xclick -= rect.x;
00197 yclick -= rect.y;
00198
00199 return pixel_position_to_hex(xpos_ + xclick, ypos_ + yclick, nearest_hex, second_nearest_hex);
00200 }
00201
00202
00203
00204 const gamemap::location display::pixel_position_to_hex(int x, int y,
00205 gamemap::location::DIRECTION* nearest_hex,
00206 gamemap::location::DIRECTION* second_nearest_hex) const
00207 {
00208
00209 x -= static_cast<int>(theme_.border().size * hex_width());
00210 y -= static_cast<int>(theme_.border().size * hex_size());
00211
00212
00213
00214 const int offset = y < 0 ? 1 : 0;
00215 if(offset) {
00216 x += hex_width();
00217 y += hex_size();
00218 }
00219 const int s = hex_size();
00220 const int tesselation_x_size = hex_width() * 2;
00221 const int tesselation_y_size = s;
00222 const int x_base = x / tesselation_x_size * 2;
00223 const int x_mod = x % tesselation_x_size;
00224 const int y_base = y / tesselation_y_size;
00225 const int y_mod = y % tesselation_y_size;
00226
00227 int x_modifier = 0;
00228 int y_modifier = 0;
00229
00230 if (y_mod < tesselation_y_size / 2) {
00231 if ((x_mod * 2 + y_mod) < (s / 2)) {
00232 x_modifier = -1;
00233 y_modifier = -1;
00234 } else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
00235 x_modifier = 0;
00236 y_modifier = 0;
00237 } else {
00238 x_modifier = 1;
00239 y_modifier = -1;
00240 }
00241
00242 } else {
00243 if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
00244 x_modifier = -1;
00245 y_modifier = 0;
00246 } else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
00247 x_modifier = 0;
00248 y_modifier = 0;
00249 } else {
00250 x_modifier = 1;
00251 y_modifier = 0;
00252 }
00253 }
00254
00255 const gamemap::location res(x_base + x_modifier - offset, y_base + y_modifier - offset);
00256
00257 if(nearest_hex != NULL) {
00258
00259
00260
00261 const int centerx = (get_location_x(res) - map_area().x + xpos_) + hex_size()/2 - hex_width();
00262 const int centery = (get_location_y(res) - map_area().y + ypos_) + hex_size()/2 - hex_size();
00263 const int x_offset = x - centerx;
00264 const int y_offset = y - centery;
00265 if(y_offset > 0) {
00266 if(x_offset > y_offset/2) {
00267 *nearest_hex = gamemap::location::SOUTH_EAST;
00268 if(second_nearest_hex != NULL) {
00269 if(x_offset/2 > y_offset) {
00270 *second_nearest_hex = gamemap::location::NORTH_EAST;
00271 } else {
00272 *second_nearest_hex = gamemap::location::SOUTH;
00273 }
00274 }
00275 } else if(-x_offset > y_offset/2) {
00276 *nearest_hex = gamemap::location::SOUTH_WEST;
00277 if(second_nearest_hex != NULL) {
00278 if(-x_offset/2 > y_offset) {
00279 *second_nearest_hex = gamemap::location::NORTH_WEST;
00280 } else {
00281 *second_nearest_hex = gamemap::location::SOUTH;
00282 }
00283 }
00284 } else {
00285 *nearest_hex = gamemap::location::SOUTH;
00286 if(second_nearest_hex != NULL) {
00287 if(x_offset > 0) {
00288 *second_nearest_hex = gamemap::location::SOUTH_EAST;
00289 } else {
00290 *second_nearest_hex = gamemap::location::SOUTH_WEST;
00291 }
00292 }
00293 }
00294 } else {
00295 if(x_offset > -y_offset/2) {
00296 *nearest_hex = gamemap::location::NORTH_EAST;
00297 if(second_nearest_hex != NULL) {
00298 if(x_offset/2 > -y_offset) {
00299 *second_nearest_hex = gamemap::location::SOUTH_EAST;
00300 } else {
00301 *second_nearest_hex = gamemap::location::NORTH;
00302 }
00303 }
00304 } else if(-x_offset > -y_offset/2) {
00305 *nearest_hex = gamemap::location::NORTH_WEST;
00306 if(second_nearest_hex != NULL) {
00307 if(-x_offset/2 > -y_offset) {
00308 *second_nearest_hex = gamemap::location::SOUTH_WEST;
00309 } else {
00310 *second_nearest_hex = gamemap::location::NORTH;
00311 }
00312 }
00313 } else {
00314 *nearest_hex = gamemap::location::NORTH;
00315 if(second_nearest_hex != NULL) {
00316 if(x_offset > 0) {
00317 *second_nearest_hex = gamemap::location::NORTH_EAST;
00318 } else {
00319 *second_nearest_hex = gamemap::location::NORTH_WEST;
00320 }
00321 }
00322 }
00323 }
00324 }
00325
00326 return res;
00327 }
00328
00329 void display::get_rect_hex_bounds(SDL_Rect rect, gamemap::location &topleft, gamemap::location &bottomright) const
00330 {
00331
00332
00333 const SDL_Rect& map_rect = map_area();
00334 rect.x -= map_rect.x;
00335 rect.y -= map_rect.y;
00336
00337
00338
00339 rect.w += map_rect.x;
00340 rect.h += map_rect.y;
00341
00342 const int tile_width = hex_width();
00343
00344
00345 topleft.x = static_cast<int>(-theme_.border().size + (xpos_ + rect.x) / tile_width);
00346 topleft.y = static_cast<int>(-theme_.border().size + (ypos_ + rect.y - (is_odd(topleft.x) ? zoom_/2 : 0)) / zoom_);
00347
00348 bottomright.x = static_cast<int>(-theme_.border().size + (xpos_ + rect.x + rect.w) / tile_width);
00349 bottomright.y = static_cast<int>(-theme_.border().size + ((ypos_ + rect.y + rect.h) - (is_odd(bottomright.x) ? zoom_/2 : 0)) / zoom_);
00350
00351
00352
00353
00354
00355
00356
00357 if(topleft.x >= -1) {
00358 topleft.x--;
00359 }
00360 if(topleft.y >= -1) {
00361 topleft.y--;
00362 }
00363 if(bottomright.x <= map_.w()) {
00364 bottomright.x++;
00365 }
00366 if(bottomright.y <= map_.h()) {
00367 bottomright.y++;
00368 }
00369 }
00370
00371 int display::get_location_x(const gamemap::location& loc) const
00372 {
00373 return static_cast<int>(map_area().x + (loc.x + theme_.border().size) * hex_width() - xpos_);
00374 }
00375
00376 int display::get_location_y(const gamemap::location& loc) const
00377 {
00378 return static_cast<int>(map_area().y + (loc.y + theme_.border().size) * zoom_ - ypos_ + (is_odd(loc.x) ? zoom_/2 : 0));
00379 }
00380
00381 gamemap::location display::minimap_location_on(int x, int y)
00382 {
00383
00384
00385
00386 if (!point_in_rect(x, y, minimap_area())) {
00387 return gamemap::location();
00388 }
00389
00390
00391
00392
00393
00394 int px = (x - minimap_location_.x) * map_.w()*hex_width() / minimap_location_.w;
00395 int py = (y - minimap_location_.y) * map_.h()*hex_size() / minimap_location_.h;
00396
00397 gamemap::location loc = pixel_position_to_hex(px, py);
00398 if (loc.x < 0)
00399 loc.x = 0;
00400 else if (loc.x >= map_.w())
00401 loc.x = map_.w() - 1;
00402
00403 if (loc.y < 0)
00404 loc.y = 0;
00405 else if (loc.y >= map_.h())
00406 loc.y = map_.h() - 1;
00407
00408 return loc;
00409 }
00410
00411 void display::get_visible_hex_bounds(gamemap::location &topleft, gamemap::location &bottomright) const
00412 {
00413 SDL_Rect r = map_area();
00414 get_rect_hex_bounds(r, topleft, bottomright);
00415 }
00416
00417 int display::screenshot(std::string filename, bool map_screenshot)
00418 {
00419 int size = 0;
00420 if (!map_screenshot) {
00421 surface screenshot_surf = screen_.getSurface();
00422 SDL_SaveBMP(screenshot_surf, filename.c_str());
00423 size = screenshot_surf->w * screenshot_surf->h;
00424 } else {
00425 if (map_.empty()) {
00426
00427 std::cerr << "No map, can't do a Map Screenshot. If it was not wanted, check your hotkey.\n";
00428 return -1;
00429 }
00430
00431 SDL_Rect area = max_map_area();
00432 map_screenshot_surf_ = create_compatible_surface(screen_.getSurface(), area.w, area.h);
00433
00434 if (map_screenshot_surf_ == NULL) {
00435
00436 std::cerr << "Can't create the screenshot surface. Maybe too big, try dezooming.\n";
00437 return -1;
00438 }
00439 size = map_screenshot_surf_->w * map_screenshot_surf_->h;
00440
00441
00442 int old_xpos = xpos_;
00443 int old_ypos = ypos_;
00444 xpos_ = 0;
00445 ypos_ = 0;
00446
00447
00448 map_screenshot_= true ;
00449 invalidateAll_ = true;
00450 DBG_DP << "draw() with map_screenshot\n";
00451 draw(true,true);
00452
00453
00454 SDL_SaveBMP(map_screenshot_surf_, filename.c_str());
00455
00456
00457 map_screenshot_surf_ = NULL;
00458
00459
00460 map_screenshot_= false;
00461 xpos_ = old_xpos;
00462 ypos_ = old_ypos;
00463
00464
00465 redraw_everything();
00466 }
00467
00468
00469 size = (2048 + size*3);
00470 return size;
00471 }
00472
00473 gui::button* display::find_button(const std::string& id)
00474 {
00475 for (size_t i = 0; i < buttons_.size(); ++i) {
00476 if(buttons_[i].id() == id) {
00477 return &buttons_[i];
00478 }
00479 }
00480 return NULL;
00481 }
00482
00483 void display::create_buttons()
00484 {
00485 std::vector<gui::button> work;
00486
00487 DBG_DP << "creating buttons...\n";
00488 const std::vector<theme::menu>& buttons = theme_.menus();
00489 for(std::vector<theme::menu>::const_iterator i = buttons.begin(); i != buttons.end(); ++i) {
00490 gui::button b(screen_,i->title(),string_to_button_type(i->type()),i->image());
00491 DBG_DP << "drawing button " << i->get_id() << "\n";
00492 b.set_id(i->get_id());
00493 const SDL_Rect& loc = i->location(screen_area());
00494 b.set_location(loc.x,loc.y);
00495 if (!i->tooltip().empty()){
00496 tooltips::add_tooltip(loc, i->tooltip());
00497 }
00498 if(rects_overlap(b.location(),map_outside_area())) {
00499 b.set_volatile(true);
00500 }
00501
00502 gui::button* b_prev = find_button(b.id());
00503 if(b_prev) b.enable(b_prev->enabled());
00504
00505 work.push_back(b);
00506 }
00507
00508 buttons_.swap(work);
00509 DBG_DP << "buttons created\n";
00510 }
00511
00512 gui::button::TYPE display::string_to_button_type(std::string type)
00513 {
00514 gui::button::TYPE res = gui::button::TYPE_PRESS;
00515 if (type == "checkbox") { res = gui::button::TYPE_CHECK; }
00516 else if (type == "image") { res = gui::button::TYPE_IMAGE; }
00517 return res;
00518 }
00519
00520 static const std::string& get_direction(size_t n)
00521 {
00522 static std::string const dirs[6] = { "-n", "-ne", "-se", "-s", "-sw", "-nw" };
00523 return dirs[n >= sizeof(dirs)/sizeof(*dirs) ? 0 : n];
00524 }
00525
00526 std::vector<std::string> display::get_fog_shroud_graphics(const gamemap::location& loc)
00527 {
00528 std::vector<std::string> res;
00529
00530 gamemap::location adjacent[6];
00531 get_adjacent_tiles(loc,adjacent);
00532 t_translation::t_terrain tiles[6];
00533
00534 static const t_translation::t_terrain terrain_types[] =
00535 { t_translation::FOGGED, t_translation::VOID_TERRAIN, t_translation::NONE_TERRAIN };
00536
00537 for(int i = 0; i != 6; ++i) {
00538 if(shrouded(adjacent[i])) {
00539 tiles[i] = t_translation::VOID_TERRAIN;
00540 } else if(!fogged(loc) && fogged(adjacent[i])) {
00541 tiles[i] = t_translation::FOGGED;
00542 } else {
00543 tiles[i] = t_translation::NONE_TERRAIN;
00544 }
00545 }
00546
00547
00548 for(const t_translation::t_terrain *terrain = terrain_types;
00549 *terrain != t_translation::NONE_TERRAIN; terrain ++) {
00550
00551
00552 int start;
00553 for(start = 0; start != 6; ++start) {
00554 if(tiles[start] != *terrain) {
00555 break;
00556 }
00557 }
00558
00559 if(start == 6) {
00560 start = 0;
00561 }
00562
00563
00564 for(int i = (start+1)%6, n = 0; i != start && n != 6; ++n) {
00565 if(tiles[i] == *terrain) {
00566 std::ostringstream stream;
00567 std::string name;
00568
00569
00570
00571
00572 stream << "terrain/" << map_.get_terrain_info(*terrain).minimap_image();
00573
00574 for(int n = 0; *terrain == tiles[i] && n != 6; i = (i+1)%6, ++n) {
00575 stream << get_direction(i);
00576
00577 if(!image::exists(stream.str() + ".png")) {
00578
00579
00580 if(name.empty()) {
00581 i = (i+1)%6;
00582 }
00583 break;
00584 } else {
00585 name = stream.str();
00586 }
00587 }
00588
00589 if(!name.empty()) {
00590 res.push_back(name + ".png");
00591 }
00592 } else {
00593 i = (i+1)%6;
00594 }
00595 }
00596 }
00597
00598 return res;
00599 }
00600
00601 std::vector<surface> display::get_terrain_images(const gamemap::location &loc,
00602 const std::string timeid,
00603 image::TYPE image_type,
00604 ADJACENT_TERRAIN_TYPE terrain_type)
00605 {
00606 std::vector<surface> res;
00607
00608 if(terrain_type == ADJACENT_FOGSHROUD) {
00609 const std::vector<std::string> fog_shroud = get_fog_shroud_graphics(loc);
00610
00611 if(!fog_shroud.empty()) {
00612 for(std::vector<std::string>::const_iterator it = fog_shroud.begin(); it != fog_shroud.end(); ++it) {
00613 image::locator image(*it);
00614
00615 const surface surface(image::get_image(image, image_type));
00616 if (!surface.null()) {
00617 res.push_back(surface);
00618 }
00619 }
00620
00621 }
00622
00623 return res;
00624 }
00625
00626 terrain_builder::ADJACENT_TERRAIN_TYPE builder_terrain_type =
00627 (terrain_type == ADJACENT_FOREGROUND ?
00628 terrain_builder::ADJACENT_FOREGROUND : terrain_builder::ADJACENT_BACKGROUND);
00629 const terrain_builder::imagelist* const terrains = builder_.get_terrain_at(loc,
00630 timeid, builder_terrain_type);
00631
00632 if(terrains != NULL) {
00633
00634
00635
00636 const std::string off_map_name = "terrain/" + theme_.border().tile_image + ".png";
00637 for(std::vector<animated<image::locator> >::const_iterator it =
00638 terrains->begin(); it != terrains->end(); ++it) {
00639
00640 image::locator image = preferences::animate_map() ?
00641 it->get_current_frame() : it->get_first_frame();
00642
00643
00644
00645
00646
00647
00648 const bool off_map = (image.get_filename() == off_map_name);
00649 const surface surface(image::get_image(image,
00650 off_map ? image::UNMASKED : image_type));
00651
00652 if (!surface.null()) {
00653 res.push_back(surface);
00654 }
00655 }
00656 }
00657
00658 return res;
00659 }
00660
00661 void display::drawing_buffer_commit()
00662 {
00663 SDL_Rect clip_rect = map_area();
00664 surface const dst(get_screen_surface());
00665 clip_rect_setter set_clip_rect(dst, clip_rect);
00666
00667
00668 for(tdrawing_buffer::const_iterator
00669 layer_itor = drawing_buffer_.begin(),
00670 layer_itor_end = drawing_buffer_.end();
00671 layer_itor != layer_itor_end; ++layer_itor) {
00672
00673 for(std::map<int, std::vector<tblit> >::const_iterator
00674 #if TDRAWING_BUFFER_USES_VECTOR
00675 drawing_iterator = layer_itor->begin(),
00676 drawing_iterator_end = layer_itor->end();
00677 #else
00678 drawing_iterator = layer_itor->second.begin(),
00679 drawing_iterator_end = layer_itor->second.end();
00680 #endif
00681 drawing_iterator != drawing_iterator_end; ++drawing_iterator) {
00682
00683 for(std::vector<tblit>::const_iterator
00684 blit_itor = drawing_iterator->second.begin(),
00685 blit_itor_end = drawing_iterator->second.end();
00686 blit_itor != blit_itor_end; ++blit_itor) {
00687
00688 for(std::vector<surface>::const_iterator
00689 surface_itor = blit_itor->surf.begin(),
00690 surface_itor_end = blit_itor->surf.end();
00691 surface_itor != surface_itor_end; ++surface_itor) {
00692
00693
00694
00695
00696 SDL_Rect dstrect = { blit_itor->x, blit_itor->y, 0, 0 };
00697
00698 if(blit_itor->clip.x || blit_itor->clip.y
00699 ||blit_itor->clip.w ||blit_itor->clip.h) {
00700
00701 SDL_Rect srcrect = blit_itor->clip;
00702 SDL_BlitSurface(*surface_itor, &srcrect, dst, &dstrect);
00703 } else {
00704 SDL_BlitSurface(*surface_itor, NULL, dst, &dstrect);
00705 }
00706 }
00707 update_rect(blit_itor->x, blit_itor->y, zoom_, zoom_);
00708 }
00709 }
00710 }
00711
00712 drawing_buffer_clear();
00713 }
00714
00715 void display::drawing_buffer_clear()
00716 {
00717 #if TDRAWING_BUFFER_USES_VECTOR
00718
00719 for(tdrawing_buffer::iterator layer_itor =
00720 drawing_buffer_.begin(),
00721 layer_itor_end = drawing_buffer_.end();
00722 layer_itor != layer_itor_end; ++layer_itor) {
00723
00724 layer_itor->clear();
00725 }
00726 #else
00727 drawing_buffer_.clear();
00728 #endif
00729 }
00730
00731 void display::sunset(const size_t delay)
00732 {
00733
00734 sunset_delay = (sunset_delay == 0 && delay == 0) ? 5 : delay;
00735 }
00736
00737 void display::toggle_benchmark()
00738 {
00739 benchmark = !benchmark;
00740 }
00741
00742 void display::flip()
00743 {
00744 if(video().faked()) {
00745 return;
00746 }
00747
00748 const surface frameBuffer = get_video_surface();
00749
00750
00751 if (sunset_delay && ++sunset_timer > sunset_delay) {
00752 sunset_timer = 0;
00753 SDL_Rect r = map_outside_area();
00754 const Uint32 color = SDL_MapRGBA(video().getSurface()->format,0,0,0,255);
00755
00756 fill_rect_alpha(r, color, 1, frameBuffer);
00757 update_rect(r);
00758 }
00759
00760 font::draw_floating_labels(frameBuffer);
00761 events::raise_volatile_draw_event();
00762 cursor::draw(frameBuffer);
00763
00764 video().flip();
00765
00766 cursor::undraw(frameBuffer);
00767 events::raise_volatile_undraw_event();
00768 font::undraw_floating_labels(frameBuffer);
00769 }
00770
00771 void display::update_display()
00772 {
00773 if(screen_.update_locked()) {
00774 return;
00775 }
00776
00777 if(preferences::show_fps() || benchmark) {
00778 static int last_sample = SDL_GetTicks();
00779 static int frames = 0;
00780 ++frames;
00781
00782 if(frames == 10) {
00783 const int this_sample = SDL_GetTicks();
00784
00785 const int fps = (frames*1000)/(this_sample - last_sample);
00786 last_sample = this_sample;
00787 frames = 0;
00788
00789 if(fps_handle_ != 0) {
00790 font::remove_floating_label(fps_handle_);
00791 fps_handle_ = 0;
00792 }
00793 std::ostringstream stream;
00794 stream << fps << "fps";
00795 fps_handle_ = font::add_floating_label(stream.str(),12,
00796 benchmark ? font::BAD_COLOUR : font::NORMAL_COLOUR,
00797 10,100,0,0,-1,screen_area(),font::LEFT_ALIGN);
00798 }
00799 } else if(fps_handle_ != 0) {
00800 font::remove_floating_label(fps_handle_);
00801 fps_handle_ = 0;
00802 }
00803
00804 flip();
00805 }
00806
00807 static void draw_panel(CVideo& video, const theme::panel& panel, std::vector<gui::button>& buttons)
00808 {
00809
00810 DBG_DP << "drawing panel " << panel.get_id() << "\n";
00811
00812 surface surf(image::get_image(panel.image()));
00813
00814 const SDL_Rect screen = screen_area();
00815 SDL_Rect& loc = panel.location(screen);
00816
00817 DBG_DP << "panel location: x=" << loc.x << ", y=" << loc.y
00818 << ", w=" << loc.w << ", h=" << loc.h << "\n";
00819
00820 if(!surf.null()) {
00821 if(surf->w != loc.w || surf->h != loc.h) {
00822 surf.assign(scale_surface(surf,loc.w,loc.h));
00823 }
00824
00825 video.blit_surface(loc.x,loc.y,surf);
00826 update_rect(loc);
00827 }
00828
00829 static bool first_time = true;
00830 for(std::vector<gui::button>::iterator b = buttons.begin(); b != buttons.end(); ++b) {
00831 if(rects_overlap(b->location(),loc)) {
00832 b->set_dirty(true);
00833 if (first_time){
00834
00835
00836
00837
00838
00839
00840
00841
00842 b->hide(true);
00843 b->hide(false);
00844 }
00845 }
00846 }
00847 }
00848
00849 static void draw_label(CVideo& video, surface target, const theme::label& label)
00850 {
00851
00852
00853 std::stringstream temp;
00854 Uint32 RGB=label.font_rgb();
00855 int red = (RGB & 0x00FF0000)>>16;
00856 int green = (RGB & 0x0000FF00)>>8;
00857 int blue = (RGB & 0x000000FF);
00858
00859 std::string c_start="<";
00860 std::string c_sep=",";
00861 std::string c_end=">";
00862 std::stringstream color;
00863 color<< c_start << red << c_sep << green << c_sep << blue << c_end;
00864 std::string text = label.text();
00865
00866 if(label.font_rgb_set()) {
00867 color<<text;
00868 text = color.str();
00869 }
00870 const std::string& icon = label.icon();
00871 SDL_Rect& loc = label.location(screen_area());
00872
00873 if(icon.empty() == false) {
00874 surface surf(image::get_image(icon));
00875 if(!surf.null()) {
00876 if(surf->w > loc.w || surf->h > loc.h) {
00877 surf.assign(scale_surface(surf,loc.w,loc.h));
00878 }
00879
00880 SDL_BlitSurface(surf,NULL,target,&loc);
00881 }
00882
00883 if(text.empty() == false) {
00884 tooltips::add_tooltip(loc,text);
00885 }
00886 } else if(text.empty() == false) {
00887 font::draw_text(&video,loc,label.font_size(),font::NORMAL_COLOUR,text,loc.x,loc.y);
00888 }
00889
00890 update_rect(loc);
00891 }
00892
00893 void display::draw_all_panels()
00894 {
00895 surface const screen(screen_.getSurface());
00896
00897 const std::vector<theme::panel>& panels = theme_.panels();
00898 for(std::vector<theme::panel>::const_iterator p = panels.begin(); p != panels.end(); ++p) {
00899 draw_panel(video(),*p,buttons_);
00900 }
00901
00902 const std::vector<theme::label>& labels = theme_.labels();
00903 for(std::vector<theme::label>::const_iterator i = labels.begin(); i != labels.end(); ++i) {
00904 draw_label(video(),screen,*i);
00905 }
00906
00907 create_buttons();
00908 }
00909
00910 static void draw_background(surface screen, const SDL_Rect& area, const std::string& image)
00911 {
00912 const surface background(image::get_image(image));
00913 if(background.null()) {
00914 return;
00915 }
00916 const unsigned int width = background->w;
00917 const unsigned int height = background->h;
00918
00919 const unsigned int w_count = static_cast<int>(std::ceil(static_cast<double>(area.w) / static_cast<double>(width)));
00920 const unsigned int h_count = static_cast<int>(std::ceil(static_cast<double>(area.h) / static_cast<double>(height)));
00921
00922 for(unsigned int w = 0, w_off = area.x; w < w_count; ++w, w_off += width) {
00923 for(unsigned int h = 0, h_off = area.y; h < h_count; ++h, h_off += height) {
00924 SDL_Rect clip = {w_off, h_off, 0, 0};
00925 SDL_BlitSurface(background, NULL, screen, &clip);
00926 }
00927 }
00928 }
00929
00930 void display::draw_text_in_hex(const gamemap::location& loc,
00931 const tdrawing_layer layer, const std::string& text,
00932 size_t font_size, SDL_Color color, double x_in_hex, double y_in_hex)
00933 {
00934 if (text.empty()) return;
00935
00936 const int drawing_order = gamemap::get_drawing_order(loc);
00937
00938 const size_t font_sz = static_cast<size_t>(font_size * get_zoom_factor()
00939 #ifdef USE_TINY_GUI
00940 / 2
00941 #endif
00942 );
00943
00944 surface text_surf = font::get_rendered_text(text, font_sz, color);
00945 surface back_surf = font::get_rendered_text(text, font_sz, font::BLACK_COLOUR);
00946 const int x = get_location_x(loc) - text_surf->w/2
00947 + static_cast<int>(x_in_hex* hex_size());
00948 const int y = get_location_y(loc) - text_surf->h/2
00949 + static_cast<int>(y_in_hex* hex_size());
00950
00951 for (int dy=-1; dy <= 1; dy++) {
00952 for (int dx=-1; dx <= 1; dx++) {
00953 if (dx!=0 || dy!=0) {
00954 drawing_buffer_add(layer, drawing_order, tblit(x + dx, y + dy, back_surf));
00955 }
00956 }
00957 }
00958 drawing_buffer_add(layer, drawing_order, tblit(x, y, text_surf));
00959 }
00960
00961 void display::clear_hex_overlay(const gamemap::location& loc)
00962 {
00963 if(! hex_overlay_.empty()) {
00964 std::map<gamemap::location, surface>::iterator itor = hex_overlay_.find(loc);
00965 if(itor != hex_overlay_.end()) {
00966 hex_overlay_.erase(itor);
00967 }
00968 }
00969 }
00970
00971 void display::render_unit_image(int x, int y, const bool ,
00972 const int drawing_order, surface image,
00973 bool hreverse, bool greyscale, fixed_t alpha,
00974 Uint32 blendto, double blend_ratio, double submerged,bool vreverse)
00975 {
00976
00977 if (image==NULL)
00978 return;
00979
00980 SDL_Rect image_rect = {x, y, image->w, image->h};
00981 SDL_Rect clip_rect = map_area();
00982 if (!rects_overlap(image_rect, clip_rect))
00983 return;
00984
00985 surface surf(image);
00986
00987 if(hreverse) {
00988 surf = image::reverse_image(surf);
00989 }
00990 if(vreverse) {
00991 surf = flop_surface(surf, false);
00992 }
00993
00994 if(greyscale) {
00995 surf = greyscale_image(surf, false);
00996 }
00997
00998 if(blend_ratio != 0) {
00999 surf = blend_surface(surf, blend_ratio, blendto, false);
01000 }
01001 if(alpha > ftofxp(1.0)) {
01002 surf = brighten_image(surf, alpha, false);
01003
01004
01005 } else if(alpha != ftofxp(1.0)) {
01006 surf = adjust_surface_alpha(surf, alpha, false);
01007 }
01008
01009 if(surf == NULL) {
01010 ERR_DP << "surface lost...\n";
01011 return;
01012 }
01013
01014 const int submerge_height = minimum<int>(surf->h,maximum<int>(0,int(surf->h*(1.0-submerged))));
01015
01016
01017 SDL_Rect srcrect = {0,0,surf->w,submerge_height};
01018
01019
01020
01021 drawing_buffer_add(LAYER_UNIT_FIRST, drawing_order, tblit(x, y, surf, srcrect));
01022
01023 if(submerge_height != surf->h) {
01024 surf.assign(adjust_surface_alpha(surf,ftofxp(0.2),false));
01025
01026 srcrect.y = submerge_height;
01027 srcrect.h = surf->h-submerge_height;
01028 y += submerge_height;
01029
01030 drawing_buffer_add(LAYER_UNIT_FIRST, drawing_order, tblit(x, y, surf, srcrect));
01031 }
01032
01033 }
01034
01035 void display::select_hex(gamemap::location hex)
01036 {
01037 invalidate(selectedHex_);
01038 selectedHex_ = hex;
01039 invalidate(selectedHex_);
01040 }
01041
01042 void display::highlight_hex(gamemap::location hex)
01043 {
01044 invalidate(mouseoverHex_);
01045 mouseoverHex_ = hex;
01046 invalidate(mouseoverHex_);
01047 }
01048
01049 void display::invalidate_locations_in_rect(SDL_Rect r)
01050 {
01051 gamemap::location topleft, bottomright;
01052 get_rect_hex_bounds(r, topleft, bottomright);
01053 for (int x = topleft.x; x <= bottomright.x; ++x) {
01054 for (int y = topleft.y; y <= bottomright.y; ++y) {
01055 gamemap::location loc(x, y);
01056 invalidate(loc);
01057 }
01058 }
01059 }
01060
01061 void display::set_diagnostic(const std::string& msg)
01062 {
01063 if(diagnostic_label_ != 0) {
01064 font::remove_floating_label(diagnostic_label_);
01065 diagnostic_label_ = 0;
01066 }
01067
01068 if(msg != "") {
01069 diagnostic_label_ = font::add_floating_label(msg,font::SIZE_PLUS,font::YELLOW_COLOUR,300.0,50.0,0.0,0.0,-1,map_outside_area());
01070 }
01071 }
01072
01073
01074
01075 bool display::draw_init()
01076 {
01077 bool changed = false;
01078
01079 if (map_.empty()) {
01080 return changed;
01081 }
01082
01083 if(benchmark) {
01084 redraw_background_ = true;
01085 invalidateAll_ = true;
01086 }
01087
01088 if(!panelsDrawn_) {
01089 draw_all_panels();
01090 panelsDrawn_ = true;
01091 changed = true;
01092 }
01093
01094 if(redraw_background_) {
01095
01096 const SDL_Rect clip_rect = map_outside_area();
01097 const surface screen = get_screen_surface();
01098 clip_rect_setter set_clip_rect(screen, clip_rect);
01099 draw_background(screen, clip_rect, theme_.border().background_image);
01100 update_rect(clip_rect);
01101
01102 redraw_background_ = false;
01103
01104
01105 invalidateAll_ = true;
01106 }
01107
01108 if(invalidateAll_) {
01109 DBG_DP << "draw() with invalidateAll\n";
01110 gamemap::location topleft;
01111 gamemap::location bottomright;
01112 get_visible_hex_bounds(topleft, bottomright);
01113 for(int x = topleft.x; x <= bottomright.x; ++x)
01114 for(int y = topleft.y; y <= bottomright.y; ++y)
01115 invalidated_.insert(gamemap::location(x,y));
01116 invalidateAll_ = false;
01117
01118 redrawMinimap_ = true;
01119 }
01120
01121 return changed;
01122 }
01123
01124 void display::draw_wrap(bool update,bool force,bool changed)
01125 {
01126 static const int time_between_draws = preferences::draw_delay();
01127 const int current_time = SDL_GetTicks();
01128 const int wait_time = nextDraw_ - current_time;
01129
01130 if(redrawMinimap_) {
01131 redrawMinimap_ = false;
01132 draw_minimap();
01133 changed = true;
01134 }
01135
01136 if(update) {
01137 if(force || changed) {
01138 if(!force && wait_time > 0) {
01139
01140 SDL_Delay(wait_time);
01141 }
01142 update_display();
01143 }
01144
01145
01146 nextDraw_ += time_between_draws;
01147
01148
01149
01150
01151
01152
01153
01154 nextDraw_ = maximum<int>(nextDraw_, SDL_GetTicks());
01155 }
01156 }
01157
01158
01159 void display::delay(unsigned int milliseconds) const
01160 {
01161 if (!game_config::no_delay)
01162 SDL_Delay(milliseconds);
01163 }
01164
01165 const theme::menu* display::menu_pressed()
01166 {
01167
01168 for(std::vector<gui::button>::iterator i = buttons_.begin(); i != buttons_.end(); ++i) {
01169 if(i->pressed()) {
01170 const size_t index = i - buttons_.begin();
01171 if(index >= theme_.menus().size()) {
01172 assert(false);
01173 return 0;
01174 }
01175 return &theme_.menus()[index];
01176 }
01177 }
01178
01179 return 0;
01180 }
01181
01182 void display::enable_menu(const std::string& item, bool enable)
01183 {
01184 for(std::vector<theme::menu>::const_iterator menu = theme_.menus().begin();
01185 menu != theme_.menus().end(); ++menu) {
01186
01187 std::vector<std::string>::const_iterator hasitem =
01188 std::find(menu->items().begin(), menu->items().end(), item);
01189
01190 if(hasitem != menu->items().end()) {
01191 const size_t index = menu - theme_.menus().begin();
01192 if(index >= buttons_.size()) {
01193 assert(false);
01194 return;
01195 }
01196 buttons_[index].enable(enable);
01197 }
01198 }
01199 }
01200
01201 void display::add_highlighted_loc(const gamemap::location &hex)
01202 {
01203
01204
01205 if (highlighted_locations_.find(hex) == highlighted_locations_.end()) {
01206 highlighted_locations_.insert(hex);
01207 invalidate(hex);
01208 }
01209 }
01210
01211 void display::clear_highlighted_locs()
01212 {
01213 for (std::set<gamemap::location>::const_iterator it = highlighted_locations_.begin();
01214 it != highlighted_locations_.end(); it++) {
01215 invalidate(*it);
01216 }
01217 highlighted_locations_.clear();
01218 }
01219
01220 void display::remove_highlighted_loc(const gamemap::location &hex)
01221 {
01222 std::set<gamemap::location>::iterator it = highlighted_locations_.find(hex);
01223
01224 if (it != highlighted_locations_.end()) {
01225 highlighted_locations_.erase(it);
01226 invalidate(hex);
01227 }
01228 }
01229
01230 void display::announce(const std::string message, const SDL_Color& colour)
01231 {
01232 font::add_floating_label(message,
01233 font::SIZE_XLARGE,
01234 colour,
01235 map_outside_area().w/2,
01236 map_outside_area().h/3,
01237 0.0,0.0,100,
01238 map_outside_area(),
01239 font::CENTER_ALIGN);
01240 }
01241
01242 void display::draw_border(const gamemap::location& loc, const int xpos, const int ypos)
01243 {
01244
01245
01246
01247
01248
01249
01250 surface screen = get_screen_surface();
01251
01252
01253 if(loc.x == -1 && loc.y == -1) {
01254 SDL_Rect rect = { xpos + zoom_/4, ypos, 3 * zoom_/4, zoom_ } ;
01255 const surface border(image::get_image(theme_.border().corner_image_top_left, image::SCALED_TO_ZOOM));
01256
01257 SDL_BlitSurface( border, NULL, screen, &rect);
01258 } else if(loc.x == map_.w() && loc.y == -1) {
01259 SDL_Rect rect = { xpos, -1, 3 * zoom_/4, zoom_ } ;
01260 surface border;
01261 if(loc.x%2 == 0) {
01262 rect.y = ypos + zoom_/2;
01263 rect.h = zoom_/2;
01264
01265
01266 border = image::get_image(theme_.border().corner_image_top_right_odd, image::SCALED_TO_ZOOM);
01267 } else {
01268 rect.y = ypos;
01269 border = image::get_image(theme_.border().corner_image_top_right_even, image::SCALED_TO_ZOOM);
01270 }
01271
01272 SDL_BlitSurface( border, NULL, screen, &rect);
01273
01274 } else if(loc.x == -1 && loc.y == map_.h()) {
01275 SDL_Rect rect = { xpos + zoom_/4, ypos, 3 * zoom_/4, zoom_/2 } ;
01276
01277 const surface border(image::get_image(theme_.border().corner_image_bottom_left, image::SCALED_TO_ZOOM));
01278
01279 SDL_BlitSurface( border, NULL, screen, &rect);
01280
01281 } else if(loc.x == map_.w() && loc.y == map_.h()) {
01282 SDL_Rect rect = { xpos, ypos, 3 * zoom_/4, zoom_/2 } ;
01283 surface border;
01284 if(loc.x%2 == 1) {
01285
01286 border = image::get_image(theme_.border().corner_image_bottom_right_even, image::SCALED_TO_ZOOM);
01287 } else {
01288 border = image::get_image(theme_.border().corner_image_bottom_right_odd, image::SCALED_TO_ZOOM);
01289 }
01290
01291 SDL_BlitSurface( border, NULL, screen, &rect);
01292
01293
01294 } else if(loc.x == -1) {
01295 SDL_Rect rect = { xpos + zoom_/4 , ypos, zoom_/2, zoom_ } ;
01296 const surface border(image::get_image(theme_.border().border_image_left, image::SCALED_TO_ZOOM));
01297
01298 SDL_BlitSurface( border, NULL, screen, &rect);
01299
01300 } else if(loc.x == map_.w()) {
01301 SDL_Rect rect = { xpos + zoom_/4 , ypos, zoom_/2, zoom_ } ;
01302 const surface border(image::get_image(theme_.border().border_image_right, image::SCALED_TO_ZOOM));
01303
01304 SDL_BlitSurface( border, NULL, screen, &rect);
01305
01306 } else if(loc.y == -1) {
01307 SDL_Rect rect = { xpos, -1, zoom_, zoom_/2 } ;
01308 surface border;
01309
01310 if(loc.x%2 == 1) {
01311 rect.y = ypos;
01312 border = image::get_image(theme_.border().border_image_top_even, image::SCALED_TO_ZOOM);
01313 } else {
01314 rect.y = ypos + zoom_/2;
01315 border = image::get_image(theme_.border().border_image_top_odd, image::SCALED_TO_ZOOM);
01316 }
01317
01318 SDL_BlitSurface( border, NULL, screen, &rect);
01319
01320 } else if(loc.y == map_.h()) {
01321 SDL_Rect rect = { xpos, -1, zoom_, zoom_/2 } ;
01322 surface border;
01323
01324 if(loc.x%2 == 1) {
01325 rect.y = ypos;
01326 border = image::get_image(theme_.border().border_image_bottom_even, image::SCALED_TO_ZOOM);
01327 } else {
01328 rect.y = ypos + zoom_/2;
01329 border = image::get_image(theme_.border().border_image_bottom_odd, image::SCALED_TO_ZOOM);
01330 }
01331
01332 SDL_BlitSurface( border, NULL, screen, &rect);
01333 }
01334 }
01335
01336 void display::draw_minimap()
01337 {
01338 const SDL_Rect& area = minimap_area();
01339 if(minimap_ == NULL || minimap_->w > area.w || minimap_->h > area.h) {
01340 minimap_ = image::getMinimap(area.w, area.h, map_, viewpoint_);
01341 if(minimap_ == NULL) {
01342 return;
01343 }
01344 }
01345
01346 const surface screen(screen_.getSurface());
01347 clip_rect_setter clip_setter(screen, area);
01348
01349 SDL_Color back_color = {31,31,23,255};
01350 draw_centered_on_background(minimap_, area, back_color, screen);
01351
01352
01353 minimap_location_.x = area.x + (area.w - minimap_->w) / 2;
01354 minimap_location_.y = area.y + (area.h - minimap_->h) / 2;
01355 minimap_location_.w = minimap_->w;
01356 minimap_location_.h = minimap_->h;
01357
01358 draw_minimap_units();
01359
01360
01361
01362 double xscaling = 1.0*minimap_->w / (map_.w()*hex_width());
01363 double yscaling = 1.0*minimap_->h / (map_.h()*hex_size());
01364
01365
01366
01367
01368 SDL_Rect map_rect = map_area();
01369 SDL_Rect map_out_rect = map_outside_area();
01370 double border = theme_.border().size;
01371 double shift_x = - border*hex_width() - (map_out_rect.w - map_rect.w) / 2;
01372 double shift_y = - (border+0.25)*hex_size() - (map_out_rect.h - map_rect.h) / 2;
01373
01374 int view_x = static_cast<int>((xpos_ + shift_x) * xscaling);
01375 int view_y = static_cast<int>((ypos_ + shift_y) * yscaling);
01376 int view_w = static_cast<int>(map_out_rect.w * xscaling);
01377 int view_h = static_cast<int>(map_out_rect.h * yscaling);
01378
01379 const Uint32 box_color = SDL_MapRGB(minimap_->format,0xFF,0xFF,0xFF);
01380 draw_rectangle(minimap_location_.x + view_x - 1,
01381 minimap_location_.y + view_y - 1,
01382 view_w + 2, view_h + 2,
01383 box_color, screen);
01384 }
01385
01386 void display::scroll(int xmove, int ymove)
01387 {
01388 const int orig_x = xpos_;
01389 const int orig_y = ypos_;
01390 xpos_ += xmove;
01391 ypos_ += ymove;
01392 bounds_check_position();
01393 const int dx = orig_x - xpos_;
01394 const int dy = orig_y - ypos_;
01395
01396
01397 if(dx == 0 && dy == 0)
01398 return;
01399
01400 map_labels_.scroll(dx, dy);
01401 font::scroll_floating_labels(dx, dy);
01402
01403 surface screen(screen_.getSurface());
01404
01405 SDL_Rect dstrect = map_area();
01406 dstrect.x += dx;
01407 dstrect.y += dy;
01408 dstrect = intersect_rects(dstrect, map_area());
01409
01410 SDL_Rect srcrect = dstrect;
01411 srcrect.x -= dx;
01412 srcrect.y -= dy;
01413
01414 SDL_BlitSurface(screen,&srcrect,screen,&dstrect);
01415
01416
01417
01418 if (dy != 0) {
01419 SDL_Rect r = map_area();
01420 r.x = 0;
01421 r.y = dy < 0 ? r.h+dy : 0;
01422 r.h = abs(dy);
01423 invalidate_locations_in_rect(r);
01424 }
01425 if (dx != 0) {
01426 SDL_Rect r = map_area();
01427 r.x = dx < 0 ? r.w+dx : 0;
01428 r.y = 0;
01429 r.w = abs(dx);
01430 invalidate_locations_in_rect(r);
01431 }
01432
01433 _scroll_event.notify_observers();
01434 update_rect(map_area());
01435
01436 redrawMinimap_ = true;
01437 }
01438
01439 void display::set_zoom(int amount)
01440 {
01441 int new_zoom = zoom_ + amount;
01442 if (new_zoom < MinZoom) {
01443 new_zoom = MinZoom;
01444 }
01445 if (new_zoom > MaxZoom) {
01446 new_zoom = MaxZoom;
01447 }
01448 if (new_zoom != zoom_) {
01449 SDL_Rect const &area = map_area();
01450 xpos_ += (xpos_ + area.w / 2) * amount / zoom_;
01451 ypos_ += (ypos_ + area.h / 2) * amount / zoom_;
01452 zoom_ = new_zoom;
01453 bounds_check_position();
01454
01455 image::set_zoom(zoom_);
01456 map_labels_.recalculate_labels();
01457 redraw_background_ = true;
01458 invalidate_all();
01459
01460
01461
01462 draw();
01463 }
01464 }
01465
01466 void display::set_default_zoom()
01467 {
01468 if (zoom_ != DefaultZoom) {
01469 last_zoom_ = zoom_;
01470 set_zoom(DefaultZoom - zoom_ );
01471 } else {
01472
01473
01474 set_zoom(last_zoom_ - zoom_);
01475 last_zoom_ = DefaultZoom;
01476 }
01477 }
01478
01479 bool display::tile_on_screen(const gamemap::location& loc)
01480 {
01481 int x = get_location_x(loc);
01482 int y = get_location_y(loc);
01483 return !outside_area(map_area(), x, y);
01484 }
01485
01486 void display::scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type)
01487 {
01488 if(screen_.update_locked()) {
01489 return;
01490 }
01491
01492 const SDL_Rect area = map_area();
01493 const int xmove_expected = screenxpos - (area.x + area.w/2);
01494 const int ymove_expected = screenypos - (area.y + area.h/2);
01495
01496 int xpos = xpos_ + xmove_expected;
01497 int ypos = ypos_ + ymove_expected;
01498 bounds_check_position(xpos, ypos);
01499 int xmove = xpos - xpos_;
01500 int ymove = ypos - ypos_;
01501
01502 if(scroll_type == WARP || turbo_speed() > 2.0 || preferences::scroll_speed() > 99) {
01503 scroll(xmove,ymove);
01504 draw();
01505 return;
01506 }
01507
01508
01509
01510 int x_old = 0;
01511 int y_old = 0;
01512
01513 const double dist_total = hypot(xmove, ymove);
01514 double dist_moved = 0.0;
01515
01516 int t_prev = SDL_GetTicks();
01517
01518 double velocity = 0.0;
01519 while (dist_moved < dist_total) {
01520 events::pump();
01521
01522 int t = SDL_GetTicks();
01523 double dt = (t - t_prev) / 1000.0;
01524 if (dt > 0.200) {
01525
01526 dt = 0.200;
01527 }
01528 t_prev = t;
01529
01530
01531
01532
01533 const double accel_time = 0.3 / turbo_speed();
01534 const double decel_time = 0.4 / turbo_speed();
01535
01536 double velocity_max = preferences::scroll_speed() * 60.0;
01537 velocity_max *= turbo_speed();
01538 double accel = velocity_max / accel_time;
01539 double decel = velocity_max / decel_time;
01540
01541
01542 double stop_time = velocity / decel;
01543 double dist_stop = dist_moved + velocity*stop_time - 0.5*decel*stop_time*stop_time;
01544 if (dist_stop > dist_total || velocity > velocity_max) {
01545 velocity -= decel * dt;
01546 if (velocity < 1.0) velocity = 1.0;
01547 } else {
01548 velocity += accel * dt;
01549 if (velocity > velocity_max) velocity = velocity_max;
01550 }
01551
01552 dist_moved += velocity * dt;
01553 if (dist_moved > dist_total) dist_moved = dist_total;
01554
01555 int x_new = round_double(xmove * dist_moved / dist_total);
01556 int y_new = round_double(ymove * dist_moved / dist_total);
01557
01558 int dx = x_new - x_old;
01559 int dy = y_new - y_old;
01560
01561 scroll(dx,dy);
01562 x_old += dx;
01563 y_old += dy;
01564 draw();
01565 }
01566 }
01567
01568 void display::scroll_to_tile(const gamemap::location& loc, SCROLL_TYPE scroll_type, bool check_fogged)
01569 {
01570 if(map_.on_board(loc) == false) {
01571 ERR_DP << "Tile at " << loc << " isn't on the map, can't scroll to the tile.\n";
01572 return;
01573 }
01574
01575 std::vector<gamemap::location> locs;
01576 locs.push_back(loc);
01577 scroll_to_tiles(locs, scroll_type, check_fogged);
01578 }
01579
01580 void display::scroll_to_tiles(gamemap::location loc1, gamemap::location loc2,
01581 SCROLL_TYPE scroll_type, bool check_fogged,
01582 double add_spacing)
01583 {
01584 std::vector<gamemap::location> locs;
01585 locs.push_back(loc1);
01586 locs.push_back(loc2);
01587 scroll_to_tiles(locs, scroll_type, check_fogged, false, add_spacing);
01588 }
01589
01590 void display::scroll_to_tiles(const std::vector<gamemap::location>& locs,
01591 SCROLL_TYPE scroll_type, bool check_fogged,
01592 bool only_if_possible, double add_spacing)
01593 {
01594
01595 int minx = 0;
01596 int maxx = 0;
01597 int miny = 0;
01598 int maxy = 0;
01599 bool valid = false;
01600 bool first_tile_on_screen = false;
01601
01602 for(std::vector<gamemap::location>::const_iterator itor = locs.begin(); itor != locs.end() ; itor++) {
01603 if(map_.on_board(*itor) == false) continue;
01604 if(check_fogged && fogged(*itor)) continue;
01605
01606 int x = get_location_x(*itor);
01607 int y = get_location_y(*itor);
01608
01609 if (!valid) {
01610 minx = x;
01611 maxx = x;
01612 miny = y;
01613 maxy = y;
01614 valid = true;
01615 first_tile_on_screen = !outside_area(map_area(), x, y);
01616 } else {
01617 int minx_new = minimum<int>(minx,x);
01618 int miny_new = minimum<int>(miny,y);
01619 int maxx_new = maximum<int>(maxx,x);
01620 int maxy_new = maximum<int>(maxy,y);
01621 SDL_Rect r = map_area();
01622 r.x = minx_new;
01623 r.y = miny_new;
01624 if(outside_area(r, maxx_new, maxy_new)) {
01625
01626 if (only_if_possible) return;
01627 break;
01628 }
01629 minx = minx_new;
01630 miny = miny_new;
01631 maxx = maxx_new;
01632 maxy = maxy_new;
01633
01634 }
01635 }
01636
01637 if(!valid) return;
01638
01639 if (scroll_type == ONSCREEN) {
01640 SDL_Rect r = map_area();
01641 int spacing = round_double(add_spacing*hex_size());
01642 r.x += spacing;
01643 r.y += spacing;
01644 r.w -= 2*spacing;
01645 r.h -= 2*spacing;
01646 if (!outside_area(r, minx,miny) && !outside_area(r, maxx,maxy)) {
01647 return;
01648 }
01649 }
01650
01651
01652 SDL_Rect locs_bbox;
01653 locs_bbox.x = minx;
01654 locs_bbox.y = miny;
01655 locs_bbox.w = maxx - minx + hex_size();
01656 locs_bbox.h = maxy - miny + hex_size();
01657
01658
01659 int target_x = locs_bbox.x + locs_bbox.w/2;
01660 int target_y = locs_bbox.y + locs_bbox.h/2;
01661
01662 if (scroll_type == ONSCREEN) {
01663
01664 SDL_Rect r = map_area();
01665 int map_center_x = r.x + r.w/2;
01666 int map_center_y = r.y + r.h/2;
01667
01668 int h = r.h;
01669 int w = r.w;
01670
01671
01672 double inside_frac = 0.5;
01673 w = (int)(w * inside_frac);
01674 h = (int)(h * inside_frac);
01675
01676
01677
01678 w -= locs_bbox.w;
01679 h -= locs_bbox.h;
01680
01681 if (w < 1) w = 1;
01682 if (h < 1) h = 1;
01683
01684 r.x = target_x - w/2;
01685 r.y = target_y - h/2;
01686 r.w = w;
01687 r.h = h;
01688
01689
01690
01691
01692
01693 if (map_center_x < r.x) {
01694 target_x = r.x;
01695 target_y = map_center_y;
01696 if (target_y < r.y) target_y = r.y;
01697 if (target_y > r.y+r.h-1) target_y = r.y+r.h-1;
01698 } else if (map_center_x > r.x+r.w-1) {
01699 target_x = r.x+r.w-1;
01700 target_y = map_center_y;
01701 if (target_y < r.y) target_y = r.y;
01702 if (target_y >= r.y+r.h) target_y = r.y+r.h-1;
01703 } else if (map_center_y < r.y) {
01704 target_y = r.y;
01705 target_x = map_center_x;
01706 if (target_x < r.x) target_x = r.x;
01707 if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
01708 } else if (map_center_y > r.y+r.h-1) {
01709 target_y = r.y+r.h-1;
01710 target_x = map_center_x;
01711 if (target_x < r.x) target_x = r.x;
01712 if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
01713 } else {
01714 ERR_DP << "Bug in the scrolling code? Looks like we would not need to scroll after all...\n";
01715
01716 }
01717 }
01718
01719 scroll_to_xy(target_x, target_y,scroll_type);
01720 }
01721
01722
01723 void display::bounds_check_position()
01724 {
01725 const int orig_zoom = zoom_;
01726
01727 if(zoom_ < MinZoom) {
01728 zoom_ = MinZoom;
01729 }
01730
01731 if(zoom_ > MaxZoom) {
01732 zoom_ = MaxZoom;
01733 }
01734
01735 bounds_check_position(xpos_, ypos_);
01736
01737 if(zoom_ != orig_zoom) {
01738 image::set_zoom(zoom_);
01739 }
01740 }
01741
01742 void display::bounds_check_position(int& xpos, int& ypos)
01743 {
01744 const int tile_width = hex_width();
01745
01746
01747 const int xend = static_cast<int>(tile_width * (map_.w() + 2 * theme_.border().size) + tile_width/3);
01748 const int yend = static_cast<int>(zoom_ * (map_.h() + 2 * theme_.border().size) + zoom_/2);
01749
01750 if(xpos > xend - map_area().w) {
01751 xpos = xend - map_area().w;
01752 }
01753
01754 if(ypos > yend - map_area().h) {
01755 ypos = yend - map_area().h;
01756 }
01757
01758 if(xpos < 0) {
01759 xpos = 0;
01760 }
01761
01762 if(ypos < 0) {
01763 ypos = 0;
01764 }
01765 }
01766
01767 void display::invalidate_all()
01768 {
01769 DBG_DP << "invalidate_all()\n";
01770 invalidateAll_ = true;
01771 invalidated_.clear();
01772 update_rect(map_area());
01773 }
01774
01775 double display::turbo_speed() const
01776 {
01777 bool res = turbo_;
01778 if(keys_[SDLK_LSHIFT] || keys_[SDLK_RSHIFT]) {
01779 res = !res;
01780 }
01781
01782 res |= screen_.faked();
01783 if (res)
01784 return turbo_speed_;
01785 else
01786 return 1.0;
01787 }
01788
01789 void display::set_idle_anim_rate(int rate)
01790 {
01791 idle_anim_rate_ = std::pow(2.0, -rate/10.0);
01792 }
01793
01794 void display::redraw_everything()
01795 {
01796 if(screen_.update_locked())
01797 return;
01798
01799 invalidateGameStatus_ = true;
01800
01801 for(size_t n = 0; n != reports::NUM_REPORTS; ++n) {
01802 reportRects_[n] = empty_rect;
01803 reportSurfaces_[n].assign(NULL);
01804 reports_[n] = reports::report();
01805 }
01806
01807 bounds_check_position();
01808
01809 tooltips::clear_tooltips();
01810
01811 theme_.set_resolution(screen_area());
01812
01813 if(buttons_.empty() == false) {
01814 create_buttons();
01815 }
01816
01817 panelsDrawn_ = false;
01818
01819 map_labels_.recalculate_labels();
01820
01821 redraw_background_ = true;
01822
01823 invalidate_all();
01824 draw(true,true);
01825 }
01826
01827 void display::draw_image_for_report(surface& img, SDL_Rect& rect)
01828 {
01829 SDL_Rect visible_area = get_non_transparent_portion(img);
01830 SDL_Rect target = rect;
01831 if(visible_area.x != 0 || visible_area.y != 0 || visible_area.w != img->w || visible_area.h != img->h) {
01832 if(visible_area.w == 0 || visible_area.h == 0) {
01833 return;
01834 }
01835
01836 if(visible_area.w > rect.w || visible_area.h > rect.h) {
01837 img.assign(get_surface_portion(img,visible_area));
01838 img.assign(scale_surface(img,rect.w,rect.h));
01839 visible_area.x = 0;
01840 visible_area.y = 0;
01841 visible_area.w = img->w;
01842 visible_area.h = img->h;
01843 } else {
01844 target.x = rect.x + (rect.w - visible_area.w)/2;
01845 target.y = rect.y + (rect.h - visible_area.h)/2;
01846 target.w = visible_area.w;
01847 target.h = visible_area.h;
01848 }
01849
01850 SDL_BlitSurface(img,&visible_area,screen_.getSurface(),&target);
01851 } else {
01852 if(img->w != rect.w || img->h != rect.h) {
01853 img.assign(scale_surface(img,rect.w,rect.h));
01854 }
01855
01856 SDL_BlitSurface(img,NULL,screen_.getSurface(),&target);
01857 }
01858 }
01859
01860 void display:: set_report_content(const reports::TYPE which_report, const std::string &content) {
01861 report_[which_report] = content;
01862 }
01863
01864 void display::refresh_report(reports::TYPE report_num, reports::report report,
01865 bool brighten)
01866 {
01867 const theme::status_item* const item = theme_.get_status_item(reports::report_name(report_num));
01868 if(item != NULL) {
01869 SDL_Rect& rect = reportRects_[report_num];
01870 const SDL_Rect& new_rect = item->location(screen_area());
01871
01872
01873 if(rect == new_rect && reports_[report_num] == report) {
01874 return;
01875 }
01876
01877 reports_[report_num] = report;
01878
01879 surface& surf = reportSurfaces_[report_num];
01880
01881 if(surf != NULL) {
01882 SDL_BlitSurface(surf,NULL,screen_.getSurface(),&rect);
01883 update_rect(rect);
01884 }
01885
01886 if(new_rect != rect || surf == NULL) {
01887 surf.assign(NULL);
01888 rect = new_rect;
01889
01890
01891
01892
01893
01894 if(rect.w > 0 && rect.h > 0) {
01895 surf.assign(get_surface_portion(screen_.getSurface(),rect));
01896 if(reportSurfaces_[report_num] == NULL) {
01897 ERR_DP << "Could not backup background for report!\n";
01898 }
01899 }
01900
01901 update_rect(rect);
01902 }
01903
01904 tooltips::clear_tooltips(rect);
01905
01906 SDL_Rect area = rect;
01907
01908 int x = rect.x, y = rect.y;
01909
01910 if(!report.empty()) {
01911
01912
01913
01914 std::stringstream temp;
01915 Uint32 RGB = item->font_rgb();
01916 int red = (RGB & 0x00FF0000)>>16;
01917 int green = (RGB & 0x0000FF00)>>8;
01918 int blue = (RGB & 0x000000FF);
01919
01920 static const std::string c_start="<";
01921 static const std::string c_sep=",";
01922 static const std::string c_end=">";
01923 std::stringstream color;
01924 color<< c_start << red << c_sep << green << c_sep << blue << c_end;
01925 std::string str;
01926
01927 str = item->prefix();
01928 if(str.empty() == false) {
01929 report.insert(report.begin(), reports::element(str,"",report.begin()->tooltip));
01930 }
01931 str = item->postfix();
01932 if(str.empty() == false) {
01933 report.push_back(reports::element(str,"",report.end()->tooltip));
01934 }
01935
01936 size_t tallest = 0;
01937 int image_count = 0;
01938 bool used_ellipsis=false;
01939 std::stringstream ellipsis_tooltip;
01940 SDL_Rect ellipsis_area =rect;
01941 for(reports::report::iterator i = report.begin(); i != report.end(); ++i) {
01942 temp.str("");
01943 if(i->text.empty() == false) {
01944 if(used_ellipsis == false) {
01945
01946 if(item->font_rgb_set()) {
01947 temp <<color.str();
01948 }
01949 temp << i->text;
01950 str = temp.str();
01951 area = font::draw_text(&screen_,rect,item->font_size(),font::NORMAL_COLOUR,str,x,y);
01952 if(area.h > tallest) {
01953 tallest = area.h;
01954 }
01955 if(i->text[i->text.size() - 1] == '\n') {
01956 x = rect.x;
01957 y += tallest;
01958 tallest = 0;
01959 } else {
01960 x += area.w;
01961 }
01962 }
01963 } else if(i->image.get_filename().empty() == false) {
01964 if(used_ellipsis == false) {
01965
01966 surface img(image::get_image(i->image));
01967
01968 if(img == NULL) {
01969 ERR_DP << "could not find image for report: '" << i->image.get_filename() << "'\n";
01970 continue;
01971 }
01972
01973 if(rect.w + rect.x - x < img->w && image_count) {
01974
01975 img=surface(image::get_image(game_config::ellipsis_image));
01976 used_ellipsis=true;
01977 }
01978
01979 area.x = x;
01980 area.y = y;
01981 area.w = minimum<int>(rect.w + rect.x - x, img->w);
01982 area.h = minimum<int>(rect.h + rect.y - y, img->h);
01983 draw_image_for_report(img, area);
01984
01985 if(brighten) {
01986 surface tod_bright(image::get_image(game_config:: tod_bright_image));
01987 if(tod_bright != NULL) {
01988 draw_image_for_report(tod_bright,area);
01989 }
01990 }
01991
01992 image_count++;
01993 if(area.h > tallest) {
01994 tallest = area.h;
01995 }
01996
01997 if(! used_ellipsis) {
01998 x += area.w;
01999 } else {
02000 ellipsis_area = area;
02001 }
02002 }
02003 } else {
02004
02005 continue;
02006 }
02007 if(i->tooltip.empty() == false) {
02008 if(! used_ellipsis) {
02009 tooltips::add_tooltip(area,i->tooltip);
02010 } else {
02011 ellipsis_tooltip<<i->tooltip<<"\n";
02012 }
02013 }
02014 }
02015 if(used_ellipsis) {
02016 tooltips::add_tooltip(ellipsis_area,ellipsis_tooltip.str());
02017 }
02018 }
02019 } else {
02020 reportSurfaces_[report_num].assign(NULL);
02021 }
02022 }
02023
02024 void display::invalidate_rectangle(const gamemap::location& first_corner, const gamemap::location& second_corner) {
02025
02026 for (int x = minimum<int>(first_corner.x,second_corner.x); x <= maximum<int>(first_corner.x,second_corner.x);x++) {
02027 for (int y = minimum<int>(first_corner.y,second_corner.y); y <= maximum<int>(first_corner.y,second_corner.y);y++) {
02028 invalidate(gamemap::location(x,y));
02029 }
02030 }
02031 }
02032
02033 void display::invalidate_zone(const int x1,const int y1, const int x2, const int y2) {
02034 const SDL_Rect& rect = map_area();
02035 invalidate_rectangle(pixel_position_to_hex(x1 - rect.x, y1 - rect.y),pixel_position_to_hex(x2 - rect.x, y2 - rect.y));
02036 }