The Battle for Wesnoth  1.17.0-dev
display.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Routines to set up the display, scroll and zoom the map.
19  */
20 
21 #include "arrow.hpp"
22 #include "cursor.hpp"
23 #include "display.hpp"
24 #include "fake_unit_manager.hpp"
25 #include "filesystem.hpp"
26 #include "font/sdl_ttf_compat.hpp"
27 #include "font/text.hpp"
28 #include "preferences/game.hpp"
29 #include "gettext.hpp"
30 #include "halo.hpp"
32 #include "language.hpp"
33 #include "log.hpp"
34 #include "map/map.hpp"
35 #include "map/label.hpp"
36 #include "minimap.hpp"
37 #include "overlay.hpp"
38 #include "play_controller.hpp" //note: this can probably be refactored out
39 #include "reports.hpp"
40 #include "resources.hpp"
41 #include "color.hpp"
42 #include "synced_context.hpp"
43 #include "team.hpp"
44 #include "terrain/builder.hpp"
45 #include "time_of_day.hpp"
46 #include "tooltips.hpp"
47 #include "tod_manager.hpp"
48 #include "units/unit.hpp"
50 #include "units/drawer.hpp"
51 #include "units/orb_status.hpp"
52 #include "whiteboard/manager.hpp"
53 #include "show_dialog.hpp"
55 
56 #include <SDL2/SDL_image.h>
57 
58 #include <algorithm>
59 #include <array>
60 #include <cmath>
61 #include <iomanip>
62 #include <utility>
63 
64 #ifdef _WIN32
65 #include <windows.h>
66 #endif
67 
68 // Includes for bug #17573
69 
70 static lg::log_domain log_display("display");
71 #define ERR_DP LOG_STREAM(err, log_display)
72 #define LOG_DP LOG_STREAM(info, log_display)
73 #define DBG_DP LOG_STREAM(debug, log_display)
74 
75 // These are macros instead of proper constants so that they auto-update if the game config is reloaded.
76 #define zoom_levels (game_config::zoom_levels)
77 #define final_zoom_index (static_cast<int>(zoom_levels.size()) - 1)
78 #define DefaultZoom (game_config::tile_size)
79 #define SmallZoom (DefaultZoom / 2)
80 #define MinZoom (zoom_levels.front())
81 #define MaxZoom (zoom_levels.back())
82 
83 namespace {
84  // if this is enabled with :benchmark, then everything is marked as invalid and redrawn each time
85  bool benchmark = false;
86 
87  bool debug_foreground = false;
88 
89  int prevLabel = 0;
90 }
91 
92 unsigned int display::zoom_ = DefaultZoom;
93 unsigned int display::last_zoom_ = SmallZoom;
94 
95 // Returns index of zoom_levels which is closest match to input zoom_level
96 // Assumption: zoom_levels is a sorted vector of ascending tile sizes
97 static int get_zoom_levels_index(unsigned int zoom_level)
98 {
99  zoom_level = std::clamp(zoom_level, MinZoom, MaxZoom); // ensure zoom_level is within zoom_levels bounds
100  auto iter = std::lower_bound(zoom_levels.begin(), zoom_levels.end(), zoom_level);
101 
102  // find closest match
103  if(iter != zoom_levels.begin() && iter != zoom_levels.end()) {
104  float diff = *iter - *(iter - 1);
105  float lower = (zoom_level - *(iter - 1)) / diff;
106  float upper = (*iter - zoom_level) / diff;
107 
108  // the previous element is closer to zoom_level than the current one
109  if(lower < upper) {
110  iter--;
111  }
112  }
113 
114  return std::distance(zoom_levels.begin(), iter);
115 }
116 
118 {
119  const team& curr_team = dc_->teams()[playing_team()];
120  const team& prev_team = playing_team() == 0
121  ? dc_->teams().back()
122  : dc_->get_team(playing_team());
123  for (const auto& i : get_overlays()) {
124  for(const overlay& ov : i.second) {
125  if(!ov.team_name.empty() &&
126  ((ov.team_name.find(curr_team.team_name()) + 1) != 0) !=
127  ((ov.team_name.find(prev_team.team_name()) + 1) != 0))
128  {
129  invalidate(i.first);
130  }
131  }
132  }
133 }
134 
135 
136 void display::add_overlay(const map_location& loc, const std::string& img, const std::string& halo, const std::string& team_name, const std::string& item_id, bool visible_under_fog, float z_order)
137 {
138  if (halo_man_) {
139  halo::handle halo_handle;
140  if(halo != "") {
141  halo_handle = halo_man_->add(get_location_x(loc) + hex_size() / 2,
142  get_location_y(loc) + hex_size() / 2, halo, loc);
143  }
144 
145  std::vector<overlay>& overlays = get_overlays()[loc];
146  auto it = std::find_if(overlays.begin(), overlays.end(), [z_order](const overlay& ov) { return ov.z_order > z_order; });
147  overlays.emplace(it, img, halo, halo_handle, team_name, item_id, visible_under_fog, z_order);
148  }
149 }
150 
152 {
153  get_overlays().erase(loc);
154 }
155 
156 void display::remove_single_overlay(const map_location& loc, const std::string& toDelete)
157 {
158  std::vector<overlay>& overlays = get_overlays()[loc];
159  overlays.erase(
160  std::remove_if(
161  overlays.begin(), overlays.end(),
162  [&toDelete](const overlay& ov) { return ov.image == toDelete || ov.halo == toDelete || ov.id == toDelete; }
163  ),
164  overlays.end()
165  );
166 }
167 
169  std::weak_ptr<wb::manager> wb,
170  reports& reports_object,
171  const config& theme_cfg,
172  const config& level,
173  bool auto_join)
174  : video2::draw_layering(auto_join)
175  , dc_(dc)
176  , halo_man_(new halo::manager(*this))
177  , wb_(wb)
180  , currentTeam_(0)
181  , dont_show_all_(false)
182  , xpos_(0)
183  , ypos_(0)
184  , view_locked_(false)
185  , theme_(theme_cfg, screen_.screen_area())
186  , zoom_index_(0)
187  , fake_unit_man_(new fake_unit_manager(*this))
188  , builder_(new terrain_builder(level, (dc_ ? &dc_->map() : nullptr), theme_.border().tile_image, theme_.border().show_border))
189  , minimap_(nullptr)
191  , redrawMinimap_(false)
192  , redraw_background_(true)
193  , invalidateAll_(true)
194  , grid_(false)
195  , diagnostic_label_(0)
196  , panelsDrawn_(false)
197  , turbo_speed_(2)
198  , turbo_(false)
199  , invalidateGameStatus_(true)
200  , map_labels_(new map_labels(nullptr))
201  , reports_object_(&reports_object)
202  , scroll_event_("scrolled")
203  , frametimes_(50)
204  , fps_counter_()
205  , fps_start_()
206  , fps_actual_()
207  , reportRects_()
208  , reportSurfaces_()
209  , reports_()
210  , menu_buttons_()
211  , action_buttons_()
212  , invalidated_()
213  , mouseover_hex_overlay_(nullptr)
214  , tod_hex_mask1(nullptr)
215  , tod_hex_mask2(nullptr)
216  , fog_images_()
217  , shroud_images_()
218  , selectedHex_()
219  , mouseoverHex_()
220  , keys_()
221  , animate_map_(true)
222  , animate_water_(true)
223  , flags_()
224  , activeTeam_(0)
225  , drawing_buffer_()
226  , map_screenshot_(false)
227  , reach_map_()
228  , reach_map_old_()
229  , reach_map_changed_(true)
230  , fps_handle_(0)
231  , invalidated_hexes_(0)
232  , drawn_hexes_(0)
234  , idle_anim_rate_(1.0)
235  , map_screenshot_surf_(nullptr)
237  , draw_coordinates_(false)
238  , draw_terrain_codes_(false)
239  , draw_num_of_bitmaps_(false)
240  , arrows_map_()
241  , color_adjust_()
242  , dirty_()
243 {
244  //The following assertion fails when starting a campaign
245  assert(singleton_ == nullptr);
246  singleton_ = this;
247 
249 
250  blindfold_ctr_ = 0;
251 
252  read(level.child_or_empty("display"));
253 
255  && (screen_.getSurface() != nullptr
256  && screen_.faked())) {
257  screen_.lock_updates(true);
258  }
259 
262 
264 
265  unsigned int tile_size = preferences::tile_size();
266  if(tile_size < MinZoom || tile_size > MaxZoom)
267  tile_size = DefaultZoom;
268  zoom_index_ = get_zoom_levels_index(tile_size);
270  if(zoom_ != preferences::tile_size()) // correct saved tile_size if necessary
272 
274 
275  init_flags();
276 
277  if(!menu_buttons_.empty() || !action_buttons_.empty()) {
278  create_buttons();
279  }
280 
281 #ifdef _WIN32
282  // Increase timer resolution to prevent delays getting much longer than they should.
283  timeBeginPeriod(1u);
284 #endif
285 }
286 
288 {
289 #ifdef _WIN32
290  timeEndPeriod(1u);
291 #endif
292 
293  singleton_ = nullptr;
294  resources::fake_units = nullptr;
295 }
296 
297 void display::set_theme(config theme_cfg) {
298  theme_ = theme(theme_cfg, screen_.screen_area());
299  builder_->set_draw_border(theme_.border().show_border);
300  menu_buttons_.clear();
301  action_buttons_.clear();
302  create_buttons();
304  rebuild_all();
306 }
307 
309 
310  flags_.clear();
311  if (!dc_) return;
312  flags_.resize(dc_->teams().size());
313 
314  std::vector<std::string> side_colors;
315  side_colors.reserve(dc_->teams().size());
316 
317  for(const team& t : dc_->teams()) {
318  std::string side_color = t.color();
319  side_colors.push_back(side_color);
320  init_flags_for_side_internal(t.side() - 1, side_color);
321  }
322 }
323 
325 {
327 }
328 
329 void display::init_flags_for_side_internal(std::size_t n, const std::string& side_color)
330 {
331  assert(dc_ != nullptr);
332  assert(n < dc_->teams().size());
333  assert(n < flags_.size());
334 
335  std::string flag = dc_->teams()[n].flag();
336  std::string old_rgb = game_config::flag_rgb;
337  std::string new_rgb = side_color;
338 
339  if(flag.empty()) {
341  }
342 
343  LOG_DP << "Adding flag for team " << n << " from animation " << flag << "\n";
344 
345  // Must recolor flag image
346  animated<image::locator> temp_anim;
347 
348  std::vector<std::string> items = utils::square_parenthetical_split(flag);
349 
350  for(const std::string& item : items) {
351  const std::vector<std::string>& sub_items = utils::split(item, ':');
352  std::string str = item;
353  int time = 100;
354 
355  if(sub_items.size() > 1) {
356  str = sub_items.front();
357  try {
358  time = std::max<int>(1, std::stoi(sub_items.back()));
359  } catch(const std::invalid_argument&) {
360  ERR_DP << "Invalid time value found when constructing flag for side " << n << ": " << sub_items.back() << "\n";
361  }
362  }
363 
364  std::stringstream temp;
365  temp << str << "~RC(" << old_rgb << ">"<< new_rgb << ")";
366  image::locator flag_image(temp.str());
367  temp_anim.add_frame(time, flag_image);
368  }
369 
371  f = temp_anim;
372  auto time = f.get_end_time();
373  if (time > 0) {
374  f.start_animation(randomness::rng::default_instance().get_random_int(0, time-1), true);
375  }
376  else {
377  // this can happen if both flag and game_config::images::flag are empty.
378  ERR_DP << "missing flag for team" << n << "\n";
379  }
380 }
381 
383 {
384  if(!get_map().is_village(loc)) {
385  return surface(nullptr);
386  }
387 
388  for (const team& t : dc_->teams()) {
389  if (t.owns_village(loc) && (!fogged(loc) || !dc_->get_team(viewing_side()).is_enemy(t.side())))
390  {
391  auto& flag = flags_[t.side() - 1];
392  flag.update_last_draw_time();
393  const image::locator &image_flag = animate_map_ ?
394  flag.get_current_frame() : flag.get_first_frame();
395  return image::get_image(image_flag, image::TOD_COLORED);
396  }
397  }
398 
399  return surface(nullptr);
400 }
401 
402 void display::set_team(std::size_t teamindex, bool show_everything)
403 {
404  assert(teamindex < dc_->teams().size());
405  currentTeam_ = teamindex;
406  if (!show_everything)
407  {
408  labels().set_team(&dc_->teams()[teamindex]);
409  dont_show_all_ = true;
410  }
411  else
412  {
413  labels().set_team(nullptr);
414  dont_show_all_ = false;
415  }
417  if(std::shared_ptr<wb::manager> w = wb_.lock())
418  w->on_viewer_change(teamindex);
419 }
420 
421 void display::set_playing_team(std::size_t teamindex)
422 {
423  assert(teamindex < dc_->teams().size());
424  activeTeam_ = teamindex;
426 }
427 
429 {
430  if (loc.valid() && exclusive_unit_draw_requests_.find(loc) == exclusive_unit_draw_requests_.end())
431  {
432  exclusive_unit_draw_requests_[loc] = unit.id();
433  return true;
434  }
435  else
436  {
437  return false;
438  }
439 }
440 
442 {
443  std::string id = "";
444  if(loc.valid())
445  {
447  //id will be set to the default "" string by the [] operator if the map doesn't have anything for that loc.
449  }
450  return id;
451 }
452 
453 const time_of_day & display::get_time_of_day(const map_location& /*loc*/) const
454 {
455  static time_of_day tod;
456  return tod;
457 }
458 
459 void display::update_tod(const time_of_day* tod_override)
460 {
461  const time_of_day* tod = tod_override;
462  if(tod == nullptr) {
463  tod = &get_time_of_day();
464  }
465 
466  const tod_color col = color_adjust_ + tod->color;
467  image::set_color_adjustment(col.r, col.g, col.b);
468 }
469 
470 void display::adjust_color_overlay(int r, int g, int b)
471 {
472  color_adjust_ = tod_color(r, g, b);
473  update_tod();
474 }
475 
476 void display::fill_images_list(const std::string& prefix, std::vector<std::string>& images)
477 {
478  if(prefix == ""){
479  return;
480  }
481 
482  // search prefix.png, prefix1.png, prefix2.png ...
483  for(int i=0; ; ++i){
484  std::ostringstream s;
485  s << prefix;
486  if(i != 0)
487  s << i;
488  s << ".png";
489  if(image::exists(s.str()))
490  images.push_back(s.str());
491  else if(i>0)
492  break;
493  }
494  if (images.empty())
495  images.emplace_back();
496 }
497 
498 const std::string& display::get_variant(const std::vector<std::string>& variants, const map_location &loc)
499 {
500  //TODO use better noise function
501  return variants[std::abs(loc.x + loc.y) % variants.size()];
502 }
503 
505 {
506  builder_->rebuild_all();
507 }
508 
510 {
511  redraw_background_ = true;
512  builder_->reload_map();
513 }
514 
516 {
517  dc_ = dc;
518  builder_->change_map(&dc_->map()); //TODO: Should display_context own and initialize the builder object?
519 }
520 
522 {
523  halo_man_.reset(new halo::manager(*this));
524 }
525 
527 {
528  halo_man_.reset(&halo_man);
529 }
530 
531 void display::blindfold(bool value)
532 {
533  if(value == true)
534  ++blindfold_ctr_;
535  else
536  --blindfold_ctr_;
537 }
538 
540 {
541  return blindfold_ctr_ > 0;
542 }
543 
544 
545 const SDL_Rect& display::max_map_area() const
546 {
547  static SDL_Rect max_area {0, 0, 0, 0};
548 
549  // hex_size() is always a multiple of 4
550  // and hex_width() a multiple of 3,
551  // so there shouldn't be off-by-one-errors
552  // due to rounding.
553  // To display a hex fully on screen,
554  // a little bit extra space is needed.
555  // Also added the border two times.
556  max_area.w = static_cast<int>((get_map().w() + 2 * theme_.border().size + 1.0/3.0) * hex_width());
557  max_area.h = static_cast<int>((get_map().h() + 2 * theme_.border().size + 0.5) * hex_size());
558 
559  return max_area;
560 }
561 
562 const SDL_Rect& display::map_area() const
563 {
564  static SDL_Rect max_area;
565  max_area = max_map_area();
566 
567  // if it's for map_screenshot, maximize and don't recenter
568  if (map_screenshot_) {
569  return max_area;
570  }
571 
572  static SDL_Rect res;
573  res = map_outside_area();
574 
575  if(max_area.w < res.w) {
576  // map is smaller, center
577  res.x += (res.w - max_area.w)/2;
578  res.w = max_area.w;
579  }
580 
581  if(max_area.h < res.h) {
582  // map is smaller, center
583  res.y += (res.h - max_area.h)/2;
584  res.h = max_area.h;
585  }
586 
587  return res;
588 }
589 
590 bool display::outside_area(const SDL_Rect& area, const int x, const int y)
591 {
592  const int x_thresh = hex_size();
593  const int y_thresh = hex_size();
594  return (x < area.x || x > area.x + area.w - x_thresh ||
595  y < area.y || y > area.y + area.h - y_thresh);
596 }
597 
598 // This function uses the screen as reference
599 const map_location display::hex_clicked_on(int xclick, int yclick) const
600 {
601  const SDL_Rect& rect = map_area();
602  if(sdl::point_in_rect(xclick,yclick,rect) == false) {
603  return map_location();
604  }
605 
606  xclick -= rect.x;
607  yclick -= rect.y;
608 
609  return pixel_position_to_hex(xpos_ + xclick, ypos_ + yclick);
610 }
611 
612 
613 // This function uses the rect of map_area as reference
615 {
616  // adjust for the border
617  x -= static_cast<int>(theme_.border().size * hex_width());
618  y -= static_cast<int>(theme_.border().size * hex_size());
619  // The editor can modify the border and this will result in a negative y
620  // value. Instead of adding extra cases we just shift the hex. Since the
621  // editor doesn't use the direction this is no problem.
622  const int offset = y < 0 ? 1 : 0;
623  if(offset) {
624  x += hex_width();
625  y += hex_size();
626  }
627  const int s = hex_size();
628  const int tesselation_x_size = hex_width() * 2;
629  const int tesselation_y_size = s;
630  const int x_base = x / tesselation_x_size * 2;
631  const int x_mod = x % tesselation_x_size;
632  const int y_base = y / tesselation_y_size;
633  const int y_mod = y % tesselation_y_size;
634 
635  int x_modifier = 0;
636  int y_modifier = 0;
637 
638  if (y_mod < tesselation_y_size / 2) {
639  if ((x_mod * 2 + y_mod) < (s / 2)) {
640  x_modifier = -1;
641  y_modifier = -1;
642  } else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
643  x_modifier = 0;
644  y_modifier = 0;
645  } else {
646  x_modifier = 1;
647  y_modifier = -1;
648  }
649 
650  } else {
651  if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
652  x_modifier = -1;
653  y_modifier = 0;
654  } else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
655  x_modifier = 0;
656  y_modifier = 0;
657  } else {
658  x_modifier = 1;
659  y_modifier = 0;
660  }
661  }
662 
663  return map_location(x_base + x_modifier - offset, y_base + y_modifier - offset);
664 }
665 
667 {
668  if (loc_.y < rect_.bottom[loc_.x & 1])
669  ++loc_.y;
670  else {
671  ++loc_.x;
672  loc_.y = rect_.top[loc_.x & 1];
673  }
674 
675  return *this;
676 }
677 
678 // begin is top left, and end is after bottom right
680 {
681  return iterator(map_location(left, top[left & 1]), *this);
682 }
684 {
685  return iterator(map_location(right+1, top[(right+1) & 1]), *this);
686 }
687 
688 const display::rect_of_hexes display::hexes_under_rect(const SDL_Rect& r) const
689 {
690  rect_of_hexes res;
691 
692  if (r.w<=0 || r.h<=0) {
693  // empty rect, return dummy values giving begin=end
694  res.left = 0;
695  res.right = -1; // end is right+1
696  res.top[0] = 0;
697  res.top[1] = 0;
698  res.bottom[0] = 0;
699  res.bottom[1] = 0;
700  return res;
701  }
702 
703  SDL_Rect map_rect = map_area();
704  // translate rect coordinates from screen-based to map_area-based
705  int x = xpos_ - map_rect.x + r.x;
706  int y = ypos_ - map_rect.y + r.y;
707  // we use the "double" type to avoid important rounding error (size of an hex!)
708  // we will also need to use std::floor to avoid bad rounding at border (negative values)
709  double tile_width = hex_width();
710  double tile_size = hex_size();
711  double border = theme_.border().size;
712  // we minus "0.(3)", for horizontal imbrication.
713  // reason is: two adjacent hexes each overlap 1/4 of their width, so for
714  // grid calculation 3/4 of tile width is used, which by default gives
715  // 18/54=0.(3). Note that, while tile_width is zoom dependent, 0.(3) is not.
716  res.left = static_cast<int>(std::floor(-border + x / tile_width - 0.3333333));
717  // we remove 1 pixel of the rectangle dimensions
718  // (the rounded division take one pixel more than needed)
719  res.right = static_cast<int>(std::floor(-border + (x + r.w-1) / tile_width));
720 
721  // for odd x, we must shift up one half-hex. Since x will vary along the edge,
722  // we store here the y values for even and odd x, respectively
723  res.top[0] = static_cast<int>(std::floor(-border + y / tile_size));
724  res.top[1] = static_cast<int>(std::floor(-border + y / tile_size - 0.5));
725  res.bottom[0] = static_cast<int>(std::floor(-border + (y + r.h-1) / tile_size));
726  res.bottom[1] = static_cast<int>(std::floor(-border + (y + r.h-1) / tile_size - 0.5));
727 
728  // TODO: in some rare cases (1/16), a corner of the big rect is on a tile
729  // (the 72x72 rectangle containing the hex) but not on the hex itself
730  // Can maybe be optimized by using pixel_position_to_hex
731 
732  return res;
733 }
734 
736 {
737  return currentTeam_ < dc_->teams().size();
738 }
739 
740 bool display::shrouded(const map_location& loc) const
741 {
742  return is_blindfolded() || (dont_show_all_ && dc_->teams()[currentTeam_].shrouded(loc));
743 }
744 
745 bool display::fogged(const map_location& loc) const
746 {
747  return is_blindfolded() || (dont_show_all_ && dc_->teams()[currentTeam_].fogged(loc));
748 }
749 
751 {
752  return static_cast<int>(map_area().x + (loc.x + theme_.border().size) * hex_width() - xpos_);
753 }
754 
756 {
757  return static_cast<int>(map_area().y + (loc.y + theme_.border().size) * zoom_ - ypos_ + (is_odd(loc.x) ? zoom_/2 : 0));
758 }
759 
761 {
762  //TODO: don't return location for this,
763  // instead directly scroll to the clicked pixel position
764 
765  if (!sdl::point_in_rect(x, y, minimap_area())) {
766  return map_location();
767  }
768 
769  // we transform the coordinates from minimap to the full map image
770  // probably more adjustments to do (border, minimap shift...)
771  // but the mouse and human capacity to evaluate the rectangle center
772  // is not pixel precise.
773  int px = (x - minimap_location_.x) * get_map().w()*hex_width() / std::max (minimap_location_.w, 1);
774  int py = (y - minimap_location_.y) * get_map().h()*hex_size() / std::max(minimap_location_.h, 1);
775 
776  map_location loc = pixel_position_to_hex(px, py);
777  if (loc.x < 0)
778  loc.x = 0;
779  else if (loc.x >= get_map().w())
780  loc.x = get_map().w() - 1;
781 
782  if (loc.y < 0)
783  loc.y = 0;
784  else if (loc.y >= get_map().h())
785  loc.y = get_map().h() - 1;
786 
787  return loc;
788 }
789 
790 surface display::screenshot(bool map_screenshot)
791 {
792  if (!map_screenshot) {
793  return screen_.getSurface().clone();
794  } else {
795  if (get_map().empty()) {
796  ERR_DP << "No map loaded, cannot create a map screenshot.\n";
797  return nullptr;
798  }
799 
800  SDL_Rect area = max_map_area();
801  map_screenshot_surf_ = surface(area.w, area.h);
802 
803  if (map_screenshot_surf_ == nullptr) {
804  // Memory problem ?
805  ERR_DP << "Could not create screenshot surface, try zooming out.\n";
806  return nullptr;
807  }
808 
809  // back up the current map view position and move to top-left
810  int old_xpos = xpos_;
811  int old_ypos = ypos_;
812  xpos_ = 0;
813  ypos_ = 0;
814 
815  // we reroute render output to the screenshot surface and invalidate all
816  map_screenshot_= true;
817  invalidateAll_ = true;
818  DBG_DP << "draw() with map_screenshot\n";
819  draw(true,true);
820 
821  // restore normal rendering
822  map_screenshot_= false;
823  xpos_ = old_xpos;
824  ypos_ = old_ypos;
825 
826  // Clear map_screenshot_surf_ and return a new surface that contains the same data
827  surface surf(std::move(map_screenshot_surf_));
828  return surf;
829  }
830 }
831 
832 std::shared_ptr<gui::button> display::find_action_button(const std::string& id)
833 {
834  for (std::size_t i = 0; i < action_buttons_.size(); ++i) {
835  if(action_buttons_[i]->id() == id) {
836  return action_buttons_[i];
837  }
838  }
839  return nullptr;
840 }
841 
842 std::shared_ptr<gui::button> display::find_menu_button(const std::string& id)
843 {
844  for (std::size_t i = 0; i < menu_buttons_.size(); ++i) {
845  if(menu_buttons_[i]->id() == id) {
846  return menu_buttons_[i];
847  }
848  }
849  return nullptr;
850 }
851 
853 {
854  DBG_DP << "positioning menu buttons...\n";
855  for(const auto& menu : theme_.menus()) {
856  std::shared_ptr<gui::button> b = find_menu_button(menu.get_id());
857  if(b) {
858  const SDL_Rect& loc = menu.location(screen_.screen_area());
859  b->set_location(loc);
860  b->set_measurements(0,0);
861  b->set_label(menu.title());
862  b->set_image(menu.image());
863  }
864  }
865 
866  DBG_DP << "positioning action buttons...\n";
867  for(const auto& action : theme_.actions()) {
868  std::shared_ptr<gui::button> b = find_action_button(action.get_id());
869  if(b) {
870  const SDL_Rect& loc = action.location(screen_.screen_area());
871  b->set_location(loc);
872  b->set_measurements(0,0);
873  b->set_label(action.title());
874  b->set_image(action.image());
875  }
876  }
877 }
878 
880 {
881  std::vector<std::shared_ptr<gui::button>> menu_work;
882  std::vector<std::shared_ptr<gui::button>> action_work;
883 
884  DBG_DP << "creating menu buttons...\n";
885  for(const auto& menu : theme_.menus()) {
886  if (!menu.is_button()) continue;
887 
888  std::shared_ptr<gui::button> b(new gui::button(screen_, menu.title(), gui::button::TYPE_PRESS, menu.image(),
889  gui::button::DEFAULT_SPACE, false, menu.overlay(), font::SIZE_BUTTON_SMALL));
890  DBG_DP << "drawing button " << menu.get_id() << "\n";
891  b->join_same(this);
892  b->set_id(menu.get_id());
893  if (!menu.tooltip().empty()){
894  b->set_tooltip_string(menu.tooltip());
895  }
896 
897  std::shared_ptr<gui::button> b_prev = find_menu_button(b->id());
898  if(b_prev) {
899  b->enable(b_prev->enabled());
900  }
901 
902  menu_work.push_back(b);
903  }
904 
905  DBG_DP << "creating action buttons...\n";
906  for(const auto& action : theme_.actions()) {
907  std::shared_ptr<gui::button> b(new gui::button(screen_, action.title(), string_to_button_type(action.type()), action.image(),
908  gui::button::DEFAULT_SPACE, false, action.overlay(), font::SIZE_BUTTON_SMALL));
909 
910  DBG_DP << "drawing button " << action.get_id() << "\n";
911  b->set_id(action.get_id());
912  b->join_same(this);
913  if (!action.tooltip(0).empty()){
914  b->set_tooltip_string(action.tooltip(0));
915  }
916 
917  std::shared_ptr<gui::button> b_prev = find_action_button(b->id());
918  if(b_prev) {
919  b->enable(b_prev->enabled());
920  if (b_prev->get_type() == gui::button::TYPE_CHECK) {
921  b->set_check(b_prev->checked());
922  }
923  }
924 
925  action_work.push_back(b);
926  }
927 
928 
929  menu_buttons_.clear();
930  menu_buttons_.assign(menu_work.begin(), menu_work.end());
931  action_buttons_.clear();
932  action_buttons_.assign(action_work.begin(), action_work.end());
933 
934  layout_buttons();
935  DBG_DP << "buttons created\n";
936 }
937 
939 {
940  for (std::shared_ptr<gui::button> btn : menu_buttons_) {
941  btn->set_dirty(true);
942  btn->draw();
943  }
944 
945  for (std::shared_ptr<gui::button> btn : action_buttons_) {
946  btn->set_dirty(true);
947  btn->draw();
948  }
949 }
950 
951 
953 {
955  if (type == "checkbox") { res = gui::button::TYPE_CHECK; }
956  else if (type == "image") { res = gui::button::TYPE_IMAGE; }
957  else if (type == "radiobox") { res = gui::button::TYPE_RADIO; }
958  else if (type == "turbo") { res = gui::button::TYPE_TURBO; }
959  return res;
960 }
961 
962 static const std::string& get_direction(std::size_t n)
963 {
964  static const std::array<std::string, 6> dirs {{ "-n", "-ne", "-se", "-s", "-sw", "-nw" }};
965  return dirs[n >= dirs.size() ? 0 : n];
966 }
967 
968 std::vector<surface> display::get_fog_shroud_images(const map_location& loc, image::TYPE image_type)
969 {
970  std::vector<std::string> names;
971  const auto adjacent = get_adjacent_tiles(loc);
972 
973  enum visibility {FOG=0, SHROUD=1, CLEAR=2};
974  visibility tiles[6];
975 
976  const std::string* image_prefix[] =
978 
979  for(int i = 0; i < 6; ++i) {
980  if(shrouded(adjacent[i])) {
981  tiles[i] = SHROUD;
982  } else if(!fogged(loc) && fogged(adjacent[i])) {
983  tiles[i] = FOG;
984  } else {
985  tiles[i] = CLEAR;
986  }
987  }
988 
989  for(int v = FOG; v != CLEAR; ++v) {
990  // Find somewhere that doesn't have overlap to use as a starting point
991  int start;
992  for(start = 0; start != 6; ++start) {
993  if(tiles[start] != v) {
994  break;
995  }
996  }
997 
998  if(start == 6) {
999  // Completely surrounded by fog or shroud. This might have
1000  // a special graphic.
1001  const std::string name = *image_prefix[v] + "-all.png";
1002  if ( image::exists(name) ) {
1003  names.push_back(name);
1004  // Proceed to the next visibility (fog -> shroud -> clear).
1005  continue;
1006  }
1007  // No special graphic found. We'll just combine some other images
1008  // and hope it works out.
1009  start = 0;
1010  }
1011 
1012  // Find all the directions overlap occurs from
1013  for (int i = (start+1)%6, cap1 = 0; i != start && cap1 != 6; ++cap1) {
1014  if(tiles[i] == v) {
1015  std::ostringstream stream;
1016  std::string name;
1017  stream << *image_prefix[v];
1018 
1019  for (int cap2 = 0; v == tiles[i] && cap2 != 6; i = (i+1)%6, ++cap2) {
1020  stream << get_direction(i);
1021 
1022  if(!image::exists(stream.str() + ".png")) {
1023  // If we don't have any surface at all,
1024  // then move onto the next overlapped area
1025  if(name.empty()) {
1026  i = (i+1)%6;
1027  }
1028  break;
1029  } else {
1030  name = stream.str();
1031  }
1032  }
1033 
1034  if(!name.empty()) {
1035  names.push_back(name + ".png");
1036  }
1037  } else {
1038  i = (i+1)%6;
1039  }
1040  }
1041  }
1042 
1043  // now get the surfaces
1044  std::vector<surface> res;
1045 
1046  for (std::string& name : names) {
1047  surface surf(image::get_image(name, image_type));
1048  if (surf)
1049  res.push_back(std::move(surf));
1050  }
1051 
1052  return res;
1053 }
1054 
1056  const std::string& timeid,
1058 {
1059  terrain_image_vector_.clear();
1060 
1061  terrain_builder::TERRAIN_TYPE builder_terrain_type =
1062  (terrain_type == FOREGROUND ?
1064 
1065  const terrain_builder::imagelist* const terrains = builder_->get_terrain_at(loc,
1066  timeid, builder_terrain_type);
1067 
1069 
1070  const time_of_day& tod = get_time_of_day(loc);
1071 
1072  //get all the light transitions
1073  const auto adjs = get_adjacent_tiles(loc);
1074  std::array<const time_of_day*, 6> atods;
1075  for(size_t d = 0; d < adjs.size(); ++d){
1076  atods[d] = &get_time_of_day(adjs[d]);
1077  }
1078 
1079  for(int d=0; d<6; ++d){
1080  /*
1081  concave
1082  _____
1083  / \
1084  / atod1 \_____
1085  \ !tod / \
1086  \_____/ atod2 \
1087  / \__\ !tod /
1088  / \_____/
1089  \ tod /
1090  \_____/
1091  */
1092 
1093  const time_of_day& atod1 = *atods[d];
1094  const time_of_day& atod2 = *atods[(d + 1) % 6];
1095 
1096  if(atod1.color == tod.color || atod2.color == tod.color || atod1.color != atod2.color)
1097  continue;
1098 
1099  if(lt.empty()) {
1100  //color the full hex before adding transitions
1101  tod_color col = tod.color + color_adjust_;
1102  lt = image::get_light_string(0, col.r, col.g, col.b);
1103  }
1104 
1105  // add the directional transitions
1106  tod_color acol = atod1.color + color_adjust_;
1107  lt += image::get_light_string(d + 1, acol.r, acol.g, acol.b);
1108  }
1109  for(int d=0; d<6; ++d){
1110  /*
1111  convex 1
1112  _____
1113  / \
1114  / atod1 \_____
1115  \ !tod / \
1116  \_____/ atod2 \
1117  / \__\ tod /
1118  / \_____/
1119  \ tod /
1120  \_____/
1121  */
1122 
1123  const time_of_day& atod1 = *atods[d];
1124  const time_of_day& atod2 = *atods[(d + 1) % 6];
1125 
1126  if(atod1.color == tod.color || atod1.color == atod2.color)
1127  continue;
1128 
1129  if(lt.empty()) {
1130  //color the full hex before adding transitions
1131  tod_color col = tod.color + color_adjust_;
1132  lt = image::get_light_string(0, col.r, col.g, col.b);
1133  }
1134 
1135  // add the directional transitions
1136  tod_color acol = atod1.color + color_adjust_;
1137  lt += image::get_light_string(d + 7, acol.r, acol.g, acol.b);
1138  }
1139  for(int d=0; d<6; ++d){
1140  /*
1141  convex 2
1142  _____
1143  / \
1144  / atod1 \_____
1145  \ tod / \
1146  \_____/ atod2 \
1147  / \__\ !tod /
1148  / \_____/
1149  \ tod /
1150  \_____/
1151  */
1152 
1153  const time_of_day& atod1 = *atods[d];
1154  const time_of_day& atod2 = *atods[(d + 1) % 6];
1155 
1156  if(atod2.color == tod.color || atod1.color == atod2.color)
1157  continue;
1158 
1159  if(lt.empty()) {
1160  //color the full hex before adding transitions
1161  tod_color col = tod.color + color_adjust_;
1162  lt = image::get_light_string(0, col.r, col.g, col.b);
1163  }
1164 
1165  // add the directional transitions
1166  tod_color acol = atod2.color + color_adjust_;
1167  lt += image::get_light_string(d + 13, acol.r, acol.g, acol.b);
1168  }
1169 
1170  if(lt.empty()){
1171  tod_color col = tod.color + color_adjust_;
1172  if(!col.is_zero()){
1173  // no real lightmap needed but still color the hex
1174  lt = image::get_light_string(-1, col.r, col.g, col.b);
1175  }
1176  }
1177 
1178  if(terrains != nullptr) {
1179  // Cache the offmap name.
1180  // Since it is themeable it can change,
1181  // so don't make it static.
1182  const std::string off_map_name = "terrain/" + theme_.border().tile_image;
1183  for(const auto& terrain : *terrains) {
1184  const image::locator &image = animate_map_ ?
1185  terrain.get_current_frame() : terrain.get_first_frame();
1186 
1187  // We prevent ToD coloring and brightening of off-map tiles,
1188  // We need to test for the tile to be rendered and
1189  // not the location, since the transitions are rendered
1190  // over the offmap-terrain and these need a ToD coloring.
1191  surface surf;
1192  const bool off_map = (image.get_filename() == off_map_name || image.get_modifications().find("NO_TOD_SHIFT()") != std::string::npos);
1193 
1194  if(off_map) {
1195  surf = image::get_image(image, image::SCALED_TO_HEX);
1196  } else if(lt.empty()) {
1197  surf = image::get_image(image, image::SCALED_TO_HEX);
1198  } else {
1200  }
1201 
1202  if (surf) {
1203  terrain_image_vector_.push_back(std::move(surf));
1204  }
1205  }
1206  }
1207 }
1208 
1210  const map_location& loc, int x, int y, const surface& surf,
1211  const SDL_Rect &clip)
1212 {
1213  drawing_buffer_.emplace_back(layer, loc, x, y, surf, clip);
1214 }
1215 
1217  const map_location& loc, int x, int y,
1218  const std::vector<surface> &surf,
1219  const SDL_Rect &clip)
1220 {
1221  drawing_buffer_.emplace_back(layer, loc, x, y, surf, clip);
1222 }
1223 
1224 // FIXME: temporary method. Group splitting should be made
1225 // public into the definition of drawing_layer
1226 //
1227 // The drawing is done per layer_group, the range per group is [low, high).
1228 const std::array<display::drawing_layer, 4> display::drawing_buffer_key::layer_groups {{
1232  // Make sure the movement doesn't show above fog and reachmap.
1234 }};
1235 
1236 enum {
1237  // you may adjust the following when needed:
1238 
1239  // maximum border. 3 should be safe even if a larger border is in use somewhere
1241 
1242  // store x, y, and layer in one 32 bit integer
1243  // 4 most significant bits == layer group => 16
1245 
1246  // 10 second most significant bits == y => 1024
1248 
1249  // 1 third most significant bit == x parity => 2
1251 
1252  // 8 fourth most significant bits == layer => 256
1254 
1255  // 9 least significant bits == x / 2 => 512 (really 1024 for x)
1257 };
1258 
1260  : key_(0)
1261 {
1262  // Start with the index of last group entry...
1263  unsigned int group_i = layer_groups.size() - 1;
1264 
1265  // ...and works backwards until the group containing the specified layer is found.
1266  while(layer < layer_groups[group_i]) {
1267  --group_i;
1268  }
1269 
1270  enum {
1271  SHIFT_LAYER = BITS_FOR_X_OVER_2,
1272  SHIFT_X_PARITY = BITS_FOR_LAYER + SHIFT_LAYER,
1273  SHIFT_Y = BITS_FOR_X_PARITY + SHIFT_X_PARITY,
1274  SHIFT_LAYER_GROUP = BITS_FOR_Y + SHIFT_Y
1275  };
1276  static_assert(SHIFT_LAYER_GROUP + BITS_FOR_LAYER_GROUP == sizeof(key_) * 8, "Bit field too small");
1277 
1278  // the parity of x must be more significant than the layer but less significant than y.
1279  // Thus basically every row is split in two: First the row containing all the odd x
1280  // then the row containing all the even x. Since thus the least significant bit of x is
1281  // not required for x ordering anymore it can be shifted out to the right.
1282  const unsigned int x_parity = static_cast<unsigned int>(loc.x) & 1;
1283  key_ = (group_i << SHIFT_LAYER_GROUP) | (static_cast<unsigned int>(loc.y + MAX_BORDER) << SHIFT_Y);
1284  key_ |= (x_parity << SHIFT_X_PARITY);
1285  key_ |= (static_cast<unsigned int>(layer) << SHIFT_LAYER) | static_cast<unsigned int>(loc.x + MAX_BORDER) / 2;
1286 }
1287 
1289 {
1290  // std::list::sort() is a stable sort
1291  drawing_buffer_.sort();
1292 
1293  SDL_Rect clip_rect = map_area();
1294  surface& screen = get_screen_surface();
1295  clip_rect_setter set_clip_rect(screen, &clip_rect);
1296 
1297  /*
1298  * Info regarding the rendering algorithm.
1299  *
1300  * In order to render a hex properly it needs to be rendered per row. On
1301  * this row several layers need to be drawn at the same time. Mainly the
1302  * unit and the background terrain. This is needed since both can spill
1303  * in the next hex. The foreground terrain needs to be drawn before to
1304  * avoid decapitation a unit.
1305  *
1306  * This ended in the following priority order:
1307  * layergroup > location > layer > 'blit_helper' > surface
1308  */
1309 
1310  for (const blit_helper &blit : drawing_buffer_) {
1311  for (const surface& surf : blit.surf()) {
1312  // Note that dstrect can be changed by sdl_blit
1313  // and so a new instance should be initialized
1314  // to pass to each call to sdl_blit.
1315  SDL_Rect dstrect {blit.x(), blit.y(), 0, 0};
1316  SDL_Rect srcrect = blit.clip();
1317  SDL_Rect *srcrectArg = (srcrect.x | srcrect.y | srcrect.w | srcrect.h)
1318  ? &srcrect : nullptr;
1319  sdl_blit(surf, srcrectArg, screen, &dstrect);
1320  //NOTE: the screen part should already be marked as 'to update'
1321  }
1322  }
1324 }
1325 
1327 {
1328  drawing_buffer_.clear();
1329 }
1330 
1332 {
1333  benchmark = !benchmark;
1334 }
1335 
1337 {
1338  debug_foreground = !debug_foreground;
1339 }
1340 
1342 {
1343  if(video().faked()) {
1344  return;
1345  }
1346 
1347  surface& frameBuffer = video().getSurface();
1348 
1349  font::draw_floating_labels(frameBuffer);
1351 
1352  video().flip();
1353 
1355  font::undraw_floating_labels(frameBuffer);
1356 }
1357 
1358 // frametime is in milliseconds
1359 static unsigned calculate_fps(unsigned frametime)
1360 {
1361  return frametime != 0u ? 1000u / frametime : 999u;
1362 }
1363 
1365 {
1366  if (screen_.update_locked()) {
1367  return;
1368  }
1369 
1370  if(preferences::show_fps() || benchmark) {
1371  static int frames = 0;
1372  ++frames;
1373  const int sample_freq = 10;
1374 
1375  if(frames == sample_freq) {
1376  const auto minmax_it = std::minmax_element(frametimes_.begin(), frametimes_.end());
1377  const unsigned render_avg = std::accumulate(frametimes_.begin(), frametimes_.end(), 0) / frametimes_.size();
1378  const int avg_fps = calculate_fps(render_avg);
1379  const int max_fps = calculate_fps(*minmax_it.first);
1380  const int min_fps = calculate_fps(*minmax_it.second);
1381  fps_history_.emplace_back(min_fps, avg_fps, max_fps);
1382  frames = 0;
1383 
1384  // flush out the stored fps values every so often
1385  if(fps_history_.size() == 1000) {
1386  std::string filename = filesystem::get_user_data_dir()+"/fps_log.csv";
1387  filesystem::scoped_ostream fps_log = filesystem::ostream_file(filename, std::ios_base::binary | std::ios_base::app);
1388  for(const auto& fps : fps_history_) {
1389  *fps_log << std::get<0>(fps) << "," << std::get<1>(fps) << "," << std::get<2>(fps) << "\n";
1390  }
1391  fps_history_.clear();
1392  }
1393 
1394  if(fps_handle_ != 0) {
1396  fps_handle_ = 0;
1397  }
1398  std::ostringstream stream;
1399  stream << "<tt> min/avg/max/act</tt>\n";
1400  stream << "<tt>FPS: " << std::setfill(' ') << std::setw(3) << min_fps << '/'<< std::setw(3) << avg_fps << '/' << std::setw(3) << max_fps << '/' << std::setw(3) << fps_actual_ << "</tt>\n";
1401  stream << "<tt>Time: " << std::setfill(' ') << std::setw(3) << *minmax_it.first << '/' << std::setw(3) << render_avg << '/' << std::setw(3) << *minmax_it.second << " ms</tt>\n";
1402  if (game_config::debug) {
1403  stream << "\nhex: " << drawn_hexes_*1.0/sample_freq;
1405  stream << " (" << (invalidated_hexes_-drawn_hexes_)*1.0/sample_freq << ")";
1406  }
1407  drawn_hexes_ = 0;
1408  invalidated_hexes_ = 0;
1409 
1410  font::floating_label flabel(stream.str());
1411  flabel.set_font_size(12);
1412  flabel.set_color(benchmark ? font::BAD_COLOR : font::NORMAL_COLOR);
1413  flabel.set_position(10, 100);
1414  flabel.set_alignment(font::LEFT_ALIGN);
1415 
1417  }
1418  } else if(fps_handle_ != 0) {
1420  fps_handle_ = 0;
1421  drawn_hexes_ = 0;
1422  invalidated_hexes_ = 0;
1423  }
1424 
1425  flip();
1426 }
1427 static void draw_panel(CVideo &video, const theme::panel& panel, std::vector<std::shared_ptr<gui::button>>& /*buttons*/)
1428 {
1429  //log_scope("draw panel");
1430  DBG_DP << "drawing panel " << panel.get_id() << "\n";
1431 
1432  surface surf(image::get_image(panel.image()));
1433 
1434  const SDL_Rect screen = video.screen_area();
1435  SDL_Rect& loc = panel.location(screen);
1436 
1437  DBG_DP << "panel location: x=" << loc.x << ", y=" << loc.y
1438  << ", w=" << loc.w << ", h=" << loc.h << "\n";
1439 
1440  if(surf) {
1441  if(surf->w != loc.w || surf->h != loc.h) {
1442  surf = tile_surface(surf,loc.w,loc.h);
1443  }
1444  video.blit_surface(loc.x, loc.y, surf);
1445  }
1446 }
1447 
1448 static void draw_label(CVideo& video, surface target, const theme::label& label)
1449 {
1450  //log_scope("draw label");
1451 
1452  const std::string& text = label.text();
1453  const color_t text_color = label.font_rgb_set() ? label.font_rgb() : font::NORMAL_COLOR;
1454  const std::string& icon = label.icon();
1455  SDL_Rect& loc = label.location(video.screen_area());
1456 
1457  if(icon.empty() == false) {
1458  surface surf(image::get_image(icon));
1459  if(surf) {
1460  if(surf->w > loc.w || surf->h > loc.h) {
1461  surf = scale_surface(surf,loc.w,loc.h);
1462  }
1463 
1464  sdl_blit(surf,nullptr,target,&loc);
1465  }
1466 
1467  if(text.empty() == false) {
1468  tooltips::add_tooltip(loc,text);
1469  }
1470  } else if(text.empty() == false) {
1471  font::pango_draw_text(&video, loc, label.font_size(), text_color, text, loc.x, loc.y);
1472  }
1473 }
1474 
1476 {
1477  surface& screen(screen_.getSurface());
1478 
1479  /*
1480  * The minimap is also a panel, force it to update its contents.
1481  * This is required when the size of the minimap has been modified.
1482  */
1484 
1485  for(const auto& panel : theme_.panels()) {
1486  draw_panel(video(), panel, menu_buttons_);
1487  }
1488 
1489  for(const auto& label : theme_.labels()) {
1490  draw_label(video(), screen, label);
1491  }
1492 
1493  render_buttons();
1494 }
1495 
1496 static void draw_background(surface screen, const SDL_Rect& area, const std::string& image)
1497 {
1498  // No background image, just fill in black.
1499  if(image.empty()) {
1500  sdl::fill_rectangle(area, color_t(0, 0, 0));
1501  return;
1502  }
1503 
1504  const surface background(image::get_image(image));
1505  if(!background) {
1506  return;
1507  }
1508 
1509  const unsigned int width = background->w;
1510  const unsigned int height = background->h;
1511 
1512  const unsigned int w_count = static_cast<int>(std::ceil(static_cast<double>(area.w) / static_cast<double>(width)));
1513  const unsigned int h_count = static_cast<int>(std::ceil(static_cast<double>(area.h) / static_cast<double>(height)));
1514 
1515  for(unsigned int w = 0, w_off = area.x; w < w_count; ++w, w_off += width) {
1516  for(unsigned int h = 0, h_off = area.y; h < h_count; ++h, h_off += height) {
1517  SDL_Rect clip = sdl::create_rect(w_off, h_off, 0, 0);
1518  sdl_blit(background, nullptr, screen, &clip);
1519  }
1520  }
1521 }
1522 
1524  const drawing_layer layer,
1525  const std::string& text,
1526  std::size_t font_size,
1527  color_t color,
1528  double x_in_hex,
1529  double y_in_hex)
1530 {
1531  if (text.empty()) return;
1532 
1533  const std::size_t font_sz = static_cast<std::size_t>(font_size * get_zoom_factor());
1534 
1535  surface text_surf = font::pango_render_text(text, font_sz, color);
1536  surface back_surf = font::pango_render_text(text, font_sz, font::BLACK_COLOR);
1537  const int x = get_location_x(loc) - text_surf->w/2
1538  + static_cast<int>(x_in_hex* hex_size());
1539  const int y = get_location_y(loc) - text_surf->h/2
1540  + static_cast<int>(y_in_hex* hex_size());
1541  for (int dy=-1; dy <= 1; ++dy) {
1542  for (int dx=-1; dx <= 1; ++dx) {
1543  if (dx!=0 || dy!=0) {
1544  drawing_buffer_add(layer, loc, x + dx, y + dy, back_surf);
1545  }
1546  }
1547  }
1548  drawing_buffer_add(layer, loc, x, y, text_surf);
1549 }
1550 
1551 //TODO: convert this to use sdl::ttexture
1553  const map_location& loc, surface image,
1554  bool hreverse, bool greyscale, fixed_t alpha,
1555  color_t blendto, double blend_ratio, double submerged, bool vreverse)
1556 {
1557  if (image==nullptr)
1558  return;
1559 
1560  SDL_Rect image_rect {x, y, image->w, image->h};
1561  SDL_Rect clip_rect = map_area();
1562  if (!sdl::rects_overlap(image_rect, clip_rect))
1563  return;
1564 
1565  surface surf(image);
1566 
1567  if(hreverse) {
1568  surf = image::reverse_image(surf);
1569  }
1570  if(vreverse) {
1571  surf = flop_surface(surf);
1572  }
1573 
1574  if(greyscale) {
1575  surf = greyscale_image(surf);
1576  }
1577 
1578  if(blend_ratio != 0) {
1579  surf = blend_surface(surf, blend_ratio, blendto);
1580  }
1581  if(alpha > ftofxp(1.0)) {
1582  surf = brighten_image(surf, alpha);
1583  //} else if(alpha != 1.0 && blendto != 0) {
1584  // surf.assign(blend_surface(surf,1.0-alpha,blendto));
1585  } else if(alpha != ftofxp(1.0)) {
1586  surf = surf.clone();
1587  adjust_surface_alpha(surf, alpha);
1588  }
1589 
1590  if(surf == nullptr) {
1591  ERR_DP << "surface lost..." << std::endl;
1592  return;
1593  }
1594 
1595  if(submerged > 0.0) {
1596  // divide the surface into 2 parts
1597  const int submerge_height = std::max<int>(0, surf->h*(1.0-submerged));
1598  const int depth = surf->h - submerge_height;
1599  SDL_Rect srcrect {0, 0, surf->w, submerge_height};
1600  drawing_buffer_add(drawing_layer, loc, x, y, surf, srcrect);
1601 
1602  if(submerge_height != surf->h) {
1603  //the lower part will be transparent
1604  float alpha_base = 0.3f; // 30% alpha at surface of water
1605  float alpha_delta = 0.015f; // lose 1.5% per pixel depth
1606  alpha_delta *= zoom_ / DefaultZoom; // adjust with zoom
1607  surf = submerge_alpha(surf, depth, alpha_base, alpha_delta);
1608 
1609  srcrect.y = submerge_height;
1610  srcrect.h = surf->h-submerge_height;
1611  y += submerge_height;
1612 
1613  drawing_buffer_add(drawing_layer, loc, x, y, surf, srcrect);
1614  }
1615  } else {
1616  // simple blit
1617  drawing_buffer_add(drawing_layer, loc, x, y, surf);
1618  }
1619 }
1621 {
1623  selectedHex_ = hex;
1626 }
1627 
1629 {
1631  mouseoverHex_ = hex;
1633 }
1634 
1635 void display::set_diagnostic(const std::string& msg)
1636 {
1637  if(diagnostic_label_ != 0) {
1639  diagnostic_label_ = 0;
1640  }
1641 
1642  if(!msg.empty()) {
1643  font::floating_label flabel(msg);
1645  flabel.set_color(font::YELLOW_COLOR);
1646  flabel.set_position(300, 50);
1647  flabel.set_clip_rect(map_outside_area());
1648 
1650  }
1651 }
1652 
1654 {
1655  if (get_map().empty()) {
1656  return;
1657  }
1658 
1659  if(benchmark) {
1660  invalidateAll_ = true;
1661  }
1662 
1663  if(!panelsDrawn_) {
1664  draw_all_panels();
1665  panelsDrawn_ = true;
1666  }
1667 
1668  if(redraw_background_) {
1669  // Full redraw of the background
1670  const SDL_Rect clip_rect = map_outside_area();
1671  const surface& screen = get_screen_surface();
1672  clip_rect_setter set_clip_rect(screen, &clip_rect);
1673  SDL_FillRect(screen, &clip_rect, 0x00000000);
1674  draw_background(screen, clip_rect, theme_.border().background_image);
1675  redraw_background_ = false;
1676 
1677  // Force a full map redraw
1678  invalidateAll_ = true;
1679  }
1680 
1681  if(invalidateAll_) {
1682  DBG_DP << "draw() with invalidateAll\n";
1683 
1684  // toggle invalidateAll_ first to allow regular invalidations
1685  invalidateAll_ = false;
1687 
1688  redrawMinimap_ = true;
1689  }
1690 }
1691 
1692 void display::draw_wrap(bool update, bool force)
1693 {
1694  static int time_between_draws = preferences::draw_delay();
1695  if(time_between_draws < 0) {
1696  time_between_draws = 1000 / screen_.current_refresh_rate();
1697  }
1698 
1699  if(redrawMinimap_) {
1700  redrawMinimap_ = false;
1701  draw_minimap();
1702  }
1703 
1704  if(update) {
1705  update_display();
1706 
1707  frametimes_.push_back(SDL_GetTicks() - last_frame_finished_);
1708  fps_counter_++;
1709  using std::chrono::duration_cast;
1710  using std::chrono::seconds;
1711  using std::chrono::steady_clock;
1712  const seconds current_second = duration_cast<seconds>(steady_clock::now().time_since_epoch());
1713  if(current_second != fps_start_) {
1714  fps_start_ = current_second;
1716  fps_counter_ = 0;
1717  }
1718  int longest_frame = *std::max_element(frametimes_.begin(), frametimes_.end());
1719  int wait_time = time_between_draws - longest_frame;
1720 
1721  if(!force && !benchmark && wait_time > 0) {
1722  // If it's not time yet to draw, delay until it is
1723  SDL_Delay(wait_time);
1724  }
1725 
1726  last_frame_finished_ = SDL_GetTicks();
1727  }
1728 }
1729 
1731 {
1732  for(auto i = action_buttons_.begin(); i != action_buttons_.end(); ++i) {
1733  if((*i)->pressed()) {
1734  const std::size_t index = std::distance(action_buttons_.begin(), i);
1735  if(index >= theme_.actions().size()) {
1736  assert(false);
1737  return nullptr;
1738  }
1739  return &theme_.actions()[index];
1740  }
1741  }
1742 
1743  return nullptr;
1744 }
1745 
1747 {
1748  for(auto i = menu_buttons_.begin(); i != menu_buttons_.end(); ++i) {
1749  if((*i)->pressed()) {
1750  const std::size_t index = std::distance(menu_buttons_.begin(), i);
1751  if(index >= theme_.menus().size()) {
1752  assert(false);
1753  return nullptr;
1754  }
1755  return theme_.get_menu_item((*i)->id());
1756  }
1757  }
1758 
1759  return nullptr;
1760 }
1761 
1762 void display::enable_menu(const std::string& item, bool enable)
1763 {
1764  for(auto menu = theme_.menus().begin(); menu != theme_.menus().end(); ++menu) {
1765 
1766  const auto hasitem = std::find_if(menu->items().begin(), menu->items().end(),
1767  [&item](const config& c) { return c["id"].str() == item; }
1768  );
1769 
1770  if(hasitem != menu->items().end()) {
1771  const std::size_t index = std::distance(theme_.menus().begin(), menu);
1772  if(index >= menu_buttons_.size()) {
1773  continue;
1774  }
1775  menu_buttons_[index]->enable(enable);
1776  }
1777  }
1778 }
1779 
1780 void display::announce(const std::string& message, const color_t& color, const announce_options& options)
1781 {
1782  if(options.discard_previous) {
1783  font::remove_floating_label(prevLabel);
1784  }
1785  font::floating_label flabel(message);
1787  flabel.set_color(color);
1789  map_outside_area().y + map_outside_area().h/3);
1790  flabel.set_lifetime(options.lifetime);
1791  flabel.set_clip_rect(map_outside_area());
1792 
1793  prevLabel = font::add_floating_label(flabel);
1794 }
1795 
1797 {
1798  const SDL_Rect& area = minimap_area();
1799 
1800  if(area.w == 0 || area.h == 0) {
1801  return;
1802  }
1803 
1804  if(minimap_ == nullptr || minimap_->w > area.w || minimap_->h > area.h) {
1805  minimap_ = image::getMinimap(area.w, area.h, get_map(),
1806  dc_->teams().empty() ? nullptr : &dc_->teams()[currentTeam_],
1807  (selectedHex_.valid() && !is_blindfolded()) ? &reach_map_ : nullptr);
1808  if(minimap_ == nullptr) {
1809  return;
1810  }
1811  }
1812 
1813  const surface& screen(screen_.getSurface());
1814  clip_rect_setter clip_setter(screen, &area);
1815 
1816  color_t back_color {31,31,23,SDL_ALPHA_OPAQUE};
1817  draw_centered_on_background(minimap_, area, back_color, screen);
1818 
1819  //update the minimap location for mouse and units functions
1820  minimap_location_.x = area.x + (area.w - minimap_->w) / 2;
1821  minimap_location_.y = area.y + (area.h - minimap_->h) / 2;
1822  minimap_location_.w = minimap_->w;
1823  minimap_location_.h = minimap_->h;
1824 
1826 
1827  // calculate the visible portion of the map:
1828  // scaling between minimap and full map images
1829  double xscaling = 1.0*minimap_->w / (get_map().w()*hex_width());
1830  double yscaling = 1.0*minimap_->h / (get_map().h()*hex_size());
1831 
1832  // we need to shift with the border size
1833  // and the 0.25 from the minimap balanced drawing
1834  // and the possible difference between real map and outside off-map
1835  SDL_Rect map_rect = map_area();
1836  SDL_Rect map_out_rect = map_outside_area();
1837  double border = theme_.border().size;
1838  double shift_x = - border*hex_width() - (map_out_rect.w - map_rect.w) / 2;
1839  double shift_y = - (border+0.25)*hex_size() - (map_out_rect.h - map_rect.h) / 2;
1840 
1841  int view_x = static_cast<int>((xpos_ + shift_x) * xscaling);
1842  int view_y = static_cast<int>((ypos_ + shift_y) * yscaling);
1843  int view_w = static_cast<int>(map_out_rect.w * xscaling);
1844  int view_h = static_cast<int>(map_out_rect.h * yscaling);
1845 
1846  SDL_Rect outline_rect {
1847  minimap_location_.x + view_x - 1,
1848  minimap_location_.y + view_y - 1,
1849  view_w + 2,
1850  view_h + 2
1851  };
1852 
1853  // SDL 2.0.10's render batching changes result in the
1854  // surface's clipping rectangle being overridden even if
1855  // no render clipping rectangle set operaton was queued,
1856  // so let's not use the render API to draw the rectangle.
1857 
1858  const SDL_Rect outline_parts[] = {
1859  // top
1860  { outline_rect.x, outline_rect.y, outline_rect.w, 1 },
1861  // bottom
1862  { outline_rect.x, outline_rect.y + outline_rect.h, outline_rect.w, 1 },
1863  // left
1864  { outline_rect.x, outline_rect.y, 1, outline_rect.h },
1865  // right
1866  { outline_rect.x + outline_rect.w - 1, outline_rect.y, 1, outline_rect.h },
1867  };
1868 
1869  for(const auto& r : outline_parts) {
1870  SDL_FillRect(screen_.getSurface(), &r, 0x00FFFFFF);
1871  }
1872 }
1873 
1875 {
1876  if (!preferences::minimap_draw_units() || is_blindfolded()) return;
1877 
1878  double xscaling = 1.0 * minimap_location_.w / get_map().w();
1879  double yscaling = 1.0 * minimap_location_.h / get_map().h();
1880 
1881  for(const auto& u : dc_->units()) {
1882  if (fogged(u.get_location()) ||
1883  (dc_->teams()[currentTeam_].is_enemy(u.side()) &&
1884  u.invisible(u.get_location())) ||
1885  u.get_hidden()) {
1886  continue;
1887  }
1888 
1889  int side = u.side();
1890  color_t col = team::get_minimap_color(side);
1891 
1893  auto status = orb_status::allied;
1894  if(dc_->teams()[currentTeam_].is_enemy(side)) {
1895  status = orb_status::enemy;
1896  } else if(currentTeam_ + 1 == static_cast<unsigned>(side)) {
1897  status = dc_->unit_orb_status(u);
1898  } else {
1899  // no-op, status is already set to orb_status::allied;
1900  }
1902  }
1903 
1904  double u_x = u.get_location().x * xscaling;
1905  double u_y = (u.get_location().y + (is_odd(u.get_location().x) ? 1 : -1)/4.0) * yscaling;
1906  // use 4/3 to compensate the horizontal hexes imbrication
1907  double u_w = 4.0 / 3.0 * xscaling;
1908  double u_h = yscaling;
1909 
1910  SDL_Rect r {
1911  minimap_location_.x + int(std::round(u_x))
1912  , minimap_location_.y + int(std::round(u_y))
1913  , int(std::round(u_w))
1914  , int(std::round(u_h))
1915  };
1916 
1917  // SDL 2.0.10's render batching changes result in the
1918  // surface's clipping rectangle being overridden even if
1919  // no render clipping rectangle set operaton was queued,
1920  // so let's not use the render API to draw the rectangle.
1921 
1922  SDL_FillRect(screen_.getSurface(), &r, col.to_argb_bytes());
1923  }
1924 }
1925 
1926 bool display::scroll(int xmove, int ymove, bool force)
1927 {
1928  if(view_locked_ && !force) {
1929  return false;
1930  }
1931 
1932  // No move offset, do nothing.
1933  if(xmove == 0 && ymove == 0) {
1934  return false;
1935  }
1936 
1937  int new_x = xpos_ + xmove;
1938  int new_y = ypos_ + ymove;
1939 
1940  bounds_check_position(new_x, new_y);
1941 
1942  // Camera position doesn't change, exit.
1943  if(xpos_ == new_x && ypos_ == new_y) {
1944  return false;
1945  }
1946 
1947  const int diff_x = xpos_ - new_x;
1948  const int diff_y = ypos_ - new_y;
1949 
1950  xpos_ = new_x;
1951  ypos_ = new_y;
1952 
1953  /* Adjust floating label positions. This only affects labels whose position is anchored
1954  * to the map instead of the screen. In order to do that, we want to adjust their drawing
1955  * coordinates in the opposite direction of the screen scroll.
1956  *
1957  * The check a few lines up prevents any scrolling from happening if the camera position
1958  * doesn't change. Without that, the label still scroll even when the map edge is reached.
1959  * If that's removed, the following formula should work instead:
1960  *
1961  * const int label_[x,y]_adjust = [x,y]pos_ - new_[x,y];
1962  */
1963  font::scroll_floating_labels(diff_x, diff_y);
1964 
1966 
1967  //
1968  // NOTE: the next three blocks can be removed once we switch to accelerated rendering.
1969  //
1970 
1971  if(!screen_.update_locked()) {
1972  surface& screen(screen_.getSurface());
1973 
1974  SDL_Rect dstrect = map_area();
1975  dstrect.x += diff_x;
1976  dstrect.y += diff_y;
1977  dstrect = sdl::intersect_rects(dstrect, map_area());
1978 
1979  SDL_Rect srcrect = dstrect;
1980  srcrect.x -= diff_x;
1981  srcrect.y -= diff_y;
1982 
1983  SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_NONE);
1984  SDL_BlitSurface(screen, &srcrect, screen, &dstrect);
1985  SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_BLEND);
1986  }
1987 
1988  if(diff_y != 0) {
1989  SDL_Rect r = map_area();
1990 
1991  if(diff_y < 0) {
1992  r.y = r.y + r.h + diff_y;
1993  }
1994 
1995  r.h = std::abs(diff_y);
1997  }
1998 
1999  if(diff_x != 0) {
2000  SDL_Rect r = map_area();
2001 
2002  if(diff_x < 0) {
2003  r.x = r.x + r.w + diff_x;
2004  }
2005 
2006  r.w = std::abs(diff_x);
2008  }
2009 
2011 
2012  redrawMinimap_ = true;
2013 
2014  return true;
2015 }
2016 
2018 {
2019  return zoom_ == MaxZoom;
2020 }
2021 
2023 {
2024  return zoom_ == MinZoom;
2025 }
2026 
2027 bool display::set_zoom(bool increase)
2028 {
2029  // Ensure we don't try to access nonexistent vector indices.
2030  zoom_index_ = std::clamp(increase ? zoom_index_ + 1 : zoom_index_ - 1, 0, final_zoom_index);
2031 
2032  // No validation check is needed in the next step since we've already set the index here and
2033  // know the new zoom value is indeed valid.
2034  return set_zoom(zoom_levels[zoom_index_], false);
2035 }
2036 
2037 bool display::set_zoom(unsigned int amount, const bool validate_value_and_set_index)
2038 {
2039  unsigned int new_zoom = std::clamp(amount, MinZoom, MaxZoom);
2040 
2041  LOG_DP << "new_zoom = " << new_zoom << std::endl;
2042 
2043  if(new_zoom == zoom_) {
2044  return false;
2045  }
2046 
2047  if(validate_value_and_set_index) {
2048  zoom_index_ = get_zoom_levels_index (new_zoom);
2049  new_zoom = zoom_levels[zoom_index_];
2050  }
2051 
2052  const SDL_Rect& outside_area = map_outside_area();
2053  const SDL_Rect& area = map_area();
2054 
2055  // Turn the zoom factor to a double in order to avoid rounding errors.
2056  double zoom_factor = static_cast<double>(new_zoom) / static_cast<double>(zoom_);
2057 
2058  // INVARIANT: xpos_ + area.w == xend where xend is as in bounds_check_position()
2059  //
2060  // xpos_: Position of the leftmost visible map pixel of the viewport, in pixels.
2061  // Affected by the current zoom: this->zoom_ pixels to the hex.
2062  //
2063  // xpos_ + area.w/2: Position of the center of the viewport, in pixels.
2064  //
2065  // (xpos_ + area.w/2) * new_zoom/zoom_: Position of the center of the
2066  // viewport, as it would be under new_zoom.
2067  //
2068  // (xpos_ + area.w/2) * new_zoom/zoom_ - area.w/2: Position of the
2069  // leftmost visible map pixel, as it would be under new_zoom.
2070  xpos_ = std::round(((xpos_ + area.w / 2) * zoom_factor) - (area.w / 2));
2071  ypos_ = std::round(((ypos_ + area.h / 2) * zoom_factor) - (area.h / 2));
2072  xpos_ -= (outside_area.w - area.w) / 2;
2073  ypos_ -= (outside_area.h - area.h) / 2;
2074 
2075  zoom_ = new_zoom;
2077  if(zoom_ != DefaultZoom) {
2078  last_zoom_ = zoom_;
2079  }
2080 
2083 
2085  redraw_background_ = true;
2086  invalidate_all();
2087 
2088  // Forces a redraw after zooming.
2089  // This prevents some graphic glitches from occurring.
2090  draw();
2091 
2092  return true;
2093 }
2094 
2096 {
2097  if (zoom_ != DefaultZoom) {
2098  last_zoom_ = zoom_;
2100  } else {
2101  // When we are already at the default zoom,
2102  // switch to the last zoom used
2104  }
2105 }
2106 
2108 {
2109  int x = get_location_x(loc);
2110  int y = get_location_y(loc);
2111  return !outside_area(map_area(), x, y);
2112 }
2113 
2115 {
2116  int x = get_location_x(loc);
2117  int y = get_location_y(loc);
2118  const SDL_Rect &area = map_area();
2119  int hw = hex_width(), hs = hex_size();
2120  return x + hs >= area.x - hw && x < area.x + area.w + hw &&
2121  y + hs >= area.y - hs && y < area.y + area.h + hs;
2122 }
2123 
2124 void display::scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type, bool force)
2125 {
2126  if(!force && (view_locked_ || !preferences::scroll_to_action())) return;
2127  if(screen_.update_locked()) {
2128  return;
2129  }
2130  const SDL_Rect area = map_area();
2131  const int xmove_expected = screenxpos - (area.x + area.w/2);
2132  const int ymove_expected = screenypos - (area.y + area.h/2);
2133 
2134  int xpos = xpos_ + xmove_expected;
2135  int ypos = ypos_ + ymove_expected;
2136  bounds_check_position(xpos, ypos);
2137  int xmove = xpos - xpos_;
2138  int ymove = ypos - ypos_;
2139 
2140  if(scroll_type == WARP || scroll_type == ONSCREEN_WARP || turbo_speed() > 2.0 || preferences::scroll_speed() > 99) {
2141  scroll(xmove,ymove,true);
2142  draw();
2143  return;
2144  }
2145 
2146  // Doing an animated scroll, with acceleration etc.
2147 
2148  int x_old = 0;
2149  int y_old = 0;
2150 
2151  const double dist_total = std::hypot(xmove, ymove);
2152  double dist_moved = 0.0;
2153 
2154  int t_prev = SDL_GetTicks();
2155 
2156  double velocity = 0.0;
2157  while (dist_moved < dist_total) {
2158  events::pump();
2159 
2160  int t = SDL_GetTicks();
2161  double dt = (t - t_prev) / 1000.0;
2162  if (dt > 0.200) {
2163  // Do not skip too many frames on slow PCs
2164  dt = 0.200;
2165  }
2166  t_prev = t;
2167 
2168  const double accel_time = 0.3 / turbo_speed(); // seconds until full speed is reached
2169  const double decel_time = 0.4 / turbo_speed(); // seconds from full speed to stop
2170 
2171  double velocity_max = preferences::scroll_speed() * 60.0;
2172  velocity_max *= turbo_speed();
2173  double accel = velocity_max / accel_time;
2174  double decel = velocity_max / decel_time;
2175 
2176  // If we started to decelerate now, where would we stop?
2177  double stop_time = velocity / decel;
2178  double dist_stop = dist_moved + velocity*stop_time - 0.5*decel*stop_time*stop_time;
2179  if (dist_stop > dist_total || velocity > velocity_max) {
2180  velocity -= decel * dt;
2181  if (velocity < 1.0) velocity = 1.0;
2182  } else {
2183  velocity += accel * dt;
2184  if (velocity > velocity_max) velocity = velocity_max;
2185  }
2186 
2187  dist_moved += velocity * dt;
2188  if (dist_moved > dist_total) dist_moved = dist_total;
2189 
2190  int x_new = std::round(xmove * dist_moved / dist_total);
2191  int y_new = std::round(ymove * dist_moved / dist_total);
2192 
2193  int dx = x_new - x_old;
2194  int dy = y_new - y_old;
2195 
2196  scroll(dx,dy,true);
2197  x_old += dx;
2198  y_old += dy;
2199  draw();
2200  }
2201 }
2202 
2203 void display::scroll_to_tile(const map_location& loc, SCROLL_TYPE scroll_type, bool check_fogged, bool force)
2204 {
2205  if(get_map().on_board(loc) == false) {
2206  ERR_DP << "Tile at " << loc << " isn't on the map, can't scroll to the tile." << std::endl;
2207  return;
2208  }
2209 
2210  std::vector<map_location> locs;
2211  locs.push_back(loc);
2212  scroll_to_tiles(locs, scroll_type, check_fogged,false,0.0,force);
2213 }
2214 
2216  SCROLL_TYPE scroll_type, bool check_fogged,
2217  double add_spacing, bool force)
2218 {
2219  std::vector<map_location> locs;
2220  locs.push_back(loc1);
2221  locs.push_back(loc2);
2222  scroll_to_tiles(locs, scroll_type, check_fogged, false, add_spacing,force);
2223 }
2224 
2225 void display::scroll_to_tiles(const std::vector<map_location>::const_iterator & begin,
2226  const std::vector<map_location>::const_iterator & end,
2227  SCROLL_TYPE scroll_type, bool check_fogged,
2228  bool only_if_possible, double add_spacing, bool force)
2229 {
2230  // basically we calculate the min/max coordinates we want to have on-screen
2231  int minx = 0;
2232  int maxx = 0;
2233  int miny = 0;
2234  int maxy = 0;
2235  bool valid = false;
2236 
2237  for(std::vector<map_location>::const_iterator itor = begin; itor != end ; ++itor) {
2238  if(get_map().on_board(*itor) == false) continue;
2239  if(check_fogged && fogged(*itor)) continue;
2240 
2241  int x = get_location_x(*itor);
2242  int y = get_location_y(*itor);
2243 
2244  if (!valid) {
2245  minx = x;
2246  maxx = x;
2247  miny = y;
2248  maxy = y;
2249  valid = true;
2250  } else {
2251  int minx_new = std::min<int>(minx,x);
2252  int miny_new = std::min<int>(miny,y);
2253  int maxx_new = std::max<int>(maxx,x);
2254  int maxy_new = std::max<int>(maxy,y);
2255  SDL_Rect r = map_area();
2256  r.x = minx_new;
2257  r.y = miny_new;
2258  if(outside_area(r, maxx_new, maxy_new)) {
2259  // we cannot fit all locations to the screen
2260  if (only_if_possible) return;
2261  break;
2262  }
2263  minx = minx_new;
2264  miny = miny_new;
2265  maxx = maxx_new;
2266  maxy = maxy_new;
2267  }
2268  }
2269  //if everything is fogged or the location list is empty
2270  if(!valid) return;
2271 
2272  if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
2273  SDL_Rect r = map_area();
2274  int spacing = std::round(add_spacing * hex_size());
2275  r.x += spacing;
2276  r.y += spacing;
2277  r.w -= 2*spacing;
2278  r.h -= 2*spacing;
2279  if (!outside_area(r, minx,miny) && !outside_area(r, maxx,maxy)) {
2280  return;
2281  }
2282  }
2283 
2284  // let's do "normal" rectangle math from now on
2285  SDL_Rect locs_bbox;
2286  locs_bbox.x = minx;
2287  locs_bbox.y = miny;
2288  locs_bbox.w = maxx - minx + hex_size();
2289  locs_bbox.h = maxy - miny + hex_size();
2290 
2291  // target the center
2292  int target_x = locs_bbox.x + locs_bbox.w/2;
2293  int target_y = locs_bbox.y + locs_bbox.h/2;
2294 
2295  if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
2296  // when doing an ONSCREEN scroll we do not center the target unless needed
2297  SDL_Rect r = map_area();
2298  int map_center_x = r.x + r.w/2;
2299  int map_center_y = r.y + r.h/2;
2300 
2301  int h = r.h;
2302  int w = r.w;
2303 
2304  // we do not want to be only inside the screen rect, but center a bit more
2305  double inside_frac = 0.5; // 0.0 = always center the target, 1.0 = scroll the minimum distance
2306  w = static_cast<int>(w * inside_frac);
2307  h = static_cast<int>(h * inside_frac);
2308 
2309  // shrink the rectangle by the size of the locations rectangle we found
2310  // such that the new task to fit a point into a rectangle instead of rectangle into rectangle
2311  w -= locs_bbox.w;
2312  h -= locs_bbox.h;
2313 
2314  if (w < 1) w = 1;
2315  if (h < 1) h = 1;
2316 
2317  r.x = target_x - w/2;
2318  r.y = target_y - h/2;
2319  r.w = w;
2320  r.h = h;
2321 
2322  // now any point within r is a possible target to scroll to
2323  // we take the one with the minimum distance to map_center
2324  // which will always be at the border of r
2325 
2326  if (map_center_x < r.x) {
2327  target_x = r.x;
2328  target_y = map_center_y;
2329  if (target_y < r.y) target_y = r.y;
2330  if (target_y > r.y+r.h-1) target_y = r.y+r.h-1;
2331  } else if (map_center_x > r.x+r.w-1) {
2332  target_x = r.x+r.w-1;
2333  target_y = map_center_y;
2334  if (target_y < r.y) target_y = r.y;
2335  if (target_y >= r.y+r.h) target_y = r.y+r.h-1;
2336  } else if (map_center_y < r.y) {
2337  target_y = r.y;
2338  target_x = map_center_x;
2339  if (target_x < r.x) target_x = r.x;
2340  if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
2341  } else if (map_center_y > r.y+r.h-1) {
2342  target_y = r.y+r.h-1;
2343  target_x = map_center_x;
2344  if (target_x < r.x) target_x = r.x;
2345  if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
2346  } else {
2347  ERR_DP << "Bug in the scrolling code? Looks like we would not need to scroll after all..." << std::endl;
2348  // keep the target at the center
2349  }
2350  }
2351 
2352  scroll_to_xy(target_x, target_y,scroll_type,force);
2353 }
2354 
2355 
2357 {
2358  const unsigned int orig_zoom = zoom_;
2359 
2360  if(zoom_ < MinZoom) {
2361  zoom_ = MinZoom;
2362  }
2363 
2364  if(zoom_ > MaxZoom) {
2365  zoom_ = MaxZoom;
2366  }
2367 
2369 
2370  if(zoom_ != orig_zoom) {
2372  }
2373 }
2374 
2375 void display::bounds_check_position(int& xpos, int& ypos) const
2376 {
2377  const int tile_width = hex_width();
2378 
2379  // Adjust for the border 2 times
2380  const int xend = static_cast<int>(tile_width * (get_map().w() + 2 * theme_.border().size) + tile_width/3);
2381  const int yend = static_cast<int>(zoom_ * (get_map().h() + 2 * theme_.border().size) + zoom_/2);
2382 
2383  if(xpos > xend - map_area().w) {
2384  xpos = xend - map_area().w;
2385  }
2386 
2387  if(ypos > yend - map_area().h) {
2388  ypos = yend - map_area().h;
2389  }
2390 
2391  if(xpos < 0) {
2392  xpos = 0;
2393  }
2394 
2395  if(ypos < 0) {
2396  ypos = 0;
2397  }
2398 }
2399 
2400 double display::turbo_speed() const
2401 {
2402  bool res = turbo_;
2403  if(keys_[SDLK_LSHIFT] || keys_[SDLK_RSHIFT]) {
2404  res = !res;
2405  }
2406 
2407  res |= screen_.faked();
2408  if (res)
2409  return turbo_speed_;
2410  else
2411  return 1.0;
2412 }
2413 
2415 {
2416  idle_anim_rate_ = std::pow(2.0, -rate/10.0);
2417 }
2418 
2420 {
2421  if(screen_.update_locked())
2422  return;
2423 
2424  invalidateGameStatus_ = true;
2425 
2426  reportRects_.clear();
2427  reportSurfaces_.clear();
2428  reports_.clear();
2429 
2431 
2433 
2435 
2436  if(!menu_buttons_.empty() || !action_buttons_.empty()) {
2437  create_buttons();
2438  }
2439 
2440  if(resources::controller) {
2442  if(command_executor != nullptr) {
2443  // This function adds button overlays,
2444  // it needs to be run after recreating the buttons.
2445  command_executor->set_button_state();
2446  }
2447  }
2448 
2449  panelsDrawn_ = false;
2450  if (!gui::in_dialog()) {
2452  }
2453 
2454  redraw_background_ = true;
2455 
2456  for(std::function<void(display&)> f : redraw_observers_) {
2457  f(*this);
2458  }
2459 
2460  int ticks1 = SDL_GetTicks();
2461  invalidate_all();
2462  int ticks2 = SDL_GetTicks();
2463  draw(true,true);
2464  int ticks3 = SDL_GetTicks();
2465  LOG_DP << "invalidate and draw: " << (ticks3 - ticks2) << " and " << (ticks2 - ticks1) << "\n";
2466 }
2467 
2468 void display::add_redraw_observer(std::function<void(display&)> f)
2469 {
2470  redraw_observers_.push_back(f);
2471 }
2472 
2474 {
2475  redraw_observers_.clear();
2476 }
2477 
2479  draw(true, false);
2480 }
2481 
2482 void display::draw(bool update) {
2483  draw(update, false);
2484 }
2485 
2486 
2487 void display::draw(bool update,bool force) {
2488 // log_scope("display::draw");
2489 
2490  if (screen_.update_locked()) {
2491  return;
2492  }
2493 
2494  if (dirty_) {
2495  flip_locker flip_lock(screen_);
2496  dirty_ = false;
2498  return;
2499  }
2500 
2501  // Trigger cache rebuild when preference gets changed
2504  builder_->rebuild_cache_all();
2505  }
2506 
2508 
2509  draw_init();
2510  pre_draw();
2511  // invalidate all that needs to be invalidated
2513 
2514  if(!get_map().empty()) {
2515  //int simulate_delay = 0;
2516 
2517  /*
2518  * draw_invalidated() also invalidates the halos, so also needs to be
2519  * ran if invalidated_.empty() == true.
2520  */
2521  if(!invalidated_.empty()) {
2522  draw_invalidated();
2523  invalidated_.clear();
2524  }
2526  post_commit();
2527  draw_sidebar();
2528 
2529  // Simulate slow PC:
2530  //SDL_Delay(2*simulate_delay + rand() % 20);
2531  }
2532  draw_wrap(update, force);
2533  post_draw();
2534 }
2535 
2537 {
2538  return *map_labels_;
2539 }
2540 
2542 {
2543  return *map_labels_;
2544 }
2545 
2546 const SDL_Rect& display::get_clip_rect()
2547 {
2548  return map_area();
2549 }
2550 
2552 // log_scope("display::draw_invalidated");
2553  SDL_Rect clip_rect = get_clip_rect();
2554  surface& screen = get_screen_surface();
2555  clip_rect_setter set_clip_rect(screen, &clip_rect);
2556  for (const map_location& loc : invalidated_) {
2557  int xpos = get_location_x(loc);
2558  int ypos = get_location_y(loc);
2559 
2560  //const bool on_map = get_map().on_board(loc);
2561  SDL_Rect hex_rect = sdl::create_rect(xpos, ypos, zoom_, zoom_);
2562  if(!sdl::rects_overlap(hex_rect,clip_rect)) {
2563  continue;
2564  }
2565  draw_hex(loc);
2566  drawn_hexes_+=1;
2567  }
2568  invalidated_hexes_ += invalidated_.size();
2569 
2570  if (dc_->teams().empty())
2571  {
2572  // The unit drawer can't function without teams
2573  return;
2574  }
2575 
2576  unit_drawer drawer = unit_drawer(*this);
2577 
2578  for (const map_location& loc : invalidated_) {
2579  unit_map::const_iterator u_it = dc_->units().find(loc);
2581  if (u_it != dc_->units().end()
2582  && (request == exclusive_unit_draw_requests_.end() || request->second == u_it->id()))
2583  drawer.redraw_unit(*u_it);
2584  }
2585 
2586 }
2587 
2589  int xpos = get_location_x(loc);
2590  int ypos = get_location_y(loc);
2591  image::TYPE image_type = get_image_type(loc);
2592  const bool on_map = get_map().on_board(loc);
2593  const time_of_day& tod = get_time_of_day(loc);
2594 
2595  int num_images_fg = 0;
2596  int num_images_bg = 0;
2597 
2598  if(!shrouded(loc)) {
2599  // unshrouded terrain (the normal case)
2600  get_terrain_images(loc, tod.id, BACKGROUND); // updates terrain_image_vector_
2602  num_images_bg = terrain_image_vector_.size();
2603 
2604  get_terrain_images(loc, tod.id, FOREGROUND); // updates terrain_image_vector_
2606  num_images_fg = terrain_image_vector_.size();
2607 
2608  // Draw the grid, if that's been enabled
2609  if(grid_) {
2611  drawing_buffer_add(LAYER_GRID_TOP, loc, xpos, ypos,
2614  drawing_buffer_add(LAYER_GRID_BOTTOM, loc, xpos, ypos,
2615  image::get_image(grid_bottom, image::TOD_COLORED));
2616  }
2617  }
2618 
2619  if(!shrouded(loc)) {
2620  auto it = get_overlays().find(loc);
2621  if(it != get_overlays().end()) {
2622  std::vector<overlay>& overlays = it->second;
2623  if(overlays.size() != 0) {
2624  tod_color tod_col = tod.color + color_adjust_;
2625  image::light_string lt = image::get_light_string(-1, tod_col.r, tod_col.g, tod_col.b);
2626 
2627  for(const overlay& ov : overlays) {
2628  bool item_visible_for_team = true;
2629  if(dont_show_all_ && !ov.team_name.empty()) {
2630  //dont_show_all_ imples that viewing_team()is a valid index to get_teams()
2631  const std::string& current_team_name = get_teams()[viewing_team()].team_name();
2632  const std::vector<std::string>& current_team_names = utils::split(current_team_name);
2633  const std::vector<std::string>& team_names = utils::split(ov.team_name);
2634 
2635  item_visible_for_team = std::find_first_of(team_names.begin(), team_names.end(),
2636  current_team_names.begin(), current_team_names.end()) != team_names.end();
2637  }
2638  if(item_visible_for_team && !(fogged(loc) && !ov.visible_in_fog))
2639  {
2640  const surface surf = ov.image.find("~NO_TOD_SHIFT()") == std::string::npos ?
2642  drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos, surf);
2643  }
2644  }
2645  }
2646  }
2647  }
2648 
2649  if(!shrouded(loc)) {
2650  // village-control flags.
2651  drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos, get_flag(loc));
2652  }
2653 
2654  // Draw the time-of-day mask on top of the terrain in the hex.
2655  // tod may differ from tod if hex is illuminated.
2656  const std::string& tod_hex_mask = tod.image_mask;
2657  if(tod_hex_mask1 != nullptr || tod_hex_mask2 != nullptr) {
2660  } else if(!tod_hex_mask.empty()) {
2661  drawing_buffer_add(LAYER_TERRAIN_FG, loc, xpos, ypos,
2662  image::get_image(tod_hex_mask,image::SCALED_TO_HEX));
2663  }
2664 
2665  // Paint mouseover overlays
2666  if(loc == mouseoverHex_ && (on_map || (in_editor() && get_map().on_board_with_border(loc)))
2667  && mouseover_hex_overlay_ != nullptr) {
2669  }
2670 
2671  // Paint arrows
2672  arrows_map_t::const_iterator arrows_in_hex = arrows_map_.find(loc);
2673  if(arrows_in_hex != arrows_map_.end()) {
2674  for (arrow* const a : arrows_in_hex->second) {
2675  a->draw_hex(loc);
2676  }
2677  }
2678 
2679  // Apply shroud, fog and linger overlay
2680 
2681  if(shrouded(loc)) {
2682  // We apply void also on off-map tiles
2683  // to shroud the half-hexes too
2684  const std::string& shroud_image = get_variant(shroud_images_, loc);
2685  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
2686  image::get_image(shroud_image, image_type));
2687  } else if(fogged(loc)) {
2688  const std::string& fog_image = get_variant(fog_images_, loc);
2689  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
2690  image::get_image(fog_image, image_type));
2691  }
2692 
2693  if(!shrouded(loc)) {
2694  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos, get_fog_shroud_images(loc, image_type));
2695  }
2696 
2697  if (on_map) {
2698  if (draw_coordinates_) {
2699  int off_x = xpos + hex_size()/2;
2700  int off_y = ypos + hex_size()/2;
2701  surface text = font::pango_render_text(lexical_cast<std::string>(loc), font::SIZE_SMALL, font::NORMAL_COLOR);
2702  surface bg(text->w, text->h);
2703  SDL_Rect bg_rect {0, 0, text->w, text->h};
2704  sdl::fill_surface_rect(bg, &bg_rect, 0xaa000000);
2705  off_x -= text->w / 2;
2706  off_y -= text->h / 2;
2707  if (draw_terrain_codes_) {
2708  off_y -= text->h / 2;
2709  }
2710  if (draw_num_of_bitmaps_) {
2711  off_y -= text->h / 2;
2712  }
2713  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
2714  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
2715  }
2716  if (draw_terrain_codes_ && (game_config::debug || !shrouded(loc))) {
2717  int off_x = xpos + hex_size()/2;
2718  int off_y = ypos + hex_size()/2;
2719  surface text = font::pango_render_text(lexical_cast<std::string>(get_map().get_terrain(loc)), font::SIZE_SMALL, font::NORMAL_COLOR);
2720  surface bg(text->w, text->h);
2721  SDL_Rect bg_rect {0, 0, text->w, text->h};
2722  sdl::fill_surface_rect(bg, &bg_rect, 0xaa000000);
2723  off_x -= text->w / 2;
2724  off_y -= text->h / 2;
2726  off_y += text->h / 2;
2727  } else if (draw_num_of_bitmaps_ && !draw_coordinates_) {
2728  off_y -= text->h / 2;
2729  }
2730  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
2731  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
2732  }
2733  if (draw_num_of_bitmaps_) {
2734  int off_x = xpos + hex_size()/2;
2735  int off_y = ypos + hex_size()/2;
2736  surface text = font::pango_render_text(std::to_string(num_images_bg + num_images_fg), font::SIZE_SMALL, font::NORMAL_COLOR);
2737  surface bg(text->w, text->h);
2738  SDL_Rect bg_rect {0, 0, text->w, text->h};
2739  sdl::fill_surface_rect(bg, &bg_rect, 0xaa000000);
2740  off_x -= text->w / 2;
2741  off_y -= text->h / 2;
2742  if (draw_coordinates_) {
2743  off_y += text->h / 2;
2744  }
2745  if (draw_terrain_codes_) {
2746  off_y += text->h / 2;
2747  }
2748  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
2749  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
2750  }
2751  }
2752 
2753  if(debug_foreground) {
2754  drawing_buffer_add(LAYER_UNIT_DEFAULT, loc, xpos, ypos,
2755  image::get_image("terrain/foreground.png", image_type));
2756  }
2757 
2758 }
2759 
2761  return image::TOD_COLORED;
2762 }
2763 
2764 /*void display::draw_sidebar() {
2765 
2766 }*/
2767 
2768 void display::draw_image_for_report(surface& img, SDL_Rect& rect)
2769 {
2770  SDL_Rect visible_area = get_non_transparent_portion(img);
2771  SDL_Rect target = rect;
2772  if(visible_area.x != 0 || visible_area.y != 0 || visible_area.w != img->w || visible_area.h != img->h) {
2773  if(visible_area.w == 0 || visible_area.h == 0) {
2774  return;
2775  }
2776 
2777  if(visible_area.w > rect.w || visible_area.h > rect.h) {
2778  img = get_surface_portion(img,visible_area);
2779  img = scale_surface(img,rect.w,rect.h);
2780  visible_area.x = 0;
2781  visible_area.y = 0;
2782  visible_area.w = img->w;
2783  visible_area.h = img->h;
2784  } else {
2785  target.x = rect.x + (rect.w - visible_area.w)/2;
2786  target.y = rect.y + (rect.h - visible_area.h)/2;
2787  target.w = visible_area.w;
2788  target.h = visible_area.h;
2789  }
2790 
2791  sdl_blit(img,&visible_area,screen_.getSurface(),&target);
2792  } else {
2793  if(img->w != rect.w || img->h != rect.h) {
2794  img = scale_surface(img,rect.w,rect.h);
2795  }
2796 
2797  sdl_blit(img,nullptr,screen_.getSurface(),&target);
2798  }
2799 }
2800 
2801 /**
2802  * Redraws the specified report (if anything has changed).
2803  * If a config is not supplied, it will be generated via
2804  * reports::generate_report().
2805  */
2806 void display::refresh_report(const std::string& report_name, const config * new_cfg)
2807 {
2808  const theme::status_item *item = theme_.get_status_item(report_name);
2809  if (!item) {
2810  reportSurfaces_[report_name] = nullptr;
2811  return;
2812  }
2813 
2814  // Now we will need the config. Generate one if needed.
2815 
2817 
2818  if (resources::controller) {
2820  }
2821 
2822  reports::context temp_context = reports::context(*dc_, *this, *resources::tod_manager, wb_.lock(), mhb);
2823 
2824  const config generated_cfg = new_cfg ? config() : reports_object_->generate_report(report_name, temp_context);
2825  if ( new_cfg == nullptr )
2826  new_cfg = &generated_cfg;
2827 
2828  SDL_Rect &rect = reportRects_[report_name];
2829  const SDL_Rect &new_rect = item->location(screen_.screen_area());
2830  surface &surf = reportSurfaces_[report_name];
2831  config &report = reports_[report_name];
2832 
2833  // Report and its location is unchanged since last time. Do nothing.
2834  if (surf && rect == new_rect && report == *new_cfg) {
2835  return;
2836  }
2837 
2838  // Update the config in reports_.
2839  report = *new_cfg;
2840 
2841  if (surf) {
2842  sdl_blit(surf, nullptr, screen_.getSurface(), &rect);
2843  }
2844 
2845  // If the rectangle has just changed, assign the surface to it
2846  if (!surf || new_rect != rect)
2847  {
2848  surf = nullptr;
2849  rect = new_rect;
2850 
2851  // If the rectangle is present, and we are blitting text,
2852  // then we need to backup the surface.
2853  // (Images generally won't need backing up,
2854  // unless they are transparent, but that is done later).
2855  if (rect.w > 0 && rect.h > 0) {
2856  surf = get_surface_portion(screen_.getSurface(), rect);
2857  if (reportSurfaces_[report_name] == nullptr) {
2858  ERR_DP << "Could not backup background for report!" << std::endl;
2859  }
2860  }
2861  }
2862 
2864 
2865  if (report.empty()) return;
2866 
2867  int x = rect.x, y = rect.y;
2868 
2869  // Add prefix, postfix elements.
2870  // Make sure that they get the same tooltip
2871  // as the guys around them.
2872  std::string str = item->prefix();
2873  if (!str.empty()) {
2874  config &e = report.add_child_at("element", config(), 0);
2875  e["text"] = str;
2876  e["tooltip"] = report.child("element")["tooltip"];
2877  }
2878  str = item->postfix();
2879  if (!str.empty()) {
2880  config &e = report.add_child("element");
2881  e["text"] = str;
2882  e["tooltip"] = report.child("element", -1)["tooltip"];
2883  }
2884 
2885  // Loop through and display each report element.
2886  int tallest = 0;
2887  int image_count = 0;
2888  bool used_ellipsis = false;
2889  std::ostringstream ellipsis_tooltip;
2890  SDL_Rect ellipsis_area = rect;
2891 
2892  for (config::const_child_itors elements = report.child_range("element");
2893  elements.begin() != elements.end(); elements.pop_front())
2894  {
2895  SDL_Rect area {x, y, rect.w + rect.x - x, rect.h + rect.y - y};
2896  if (area.h <= 0) break;
2897 
2898  std::string t = elements.front()["text"];
2899  if (!t.empty())
2900  {
2901  if (used_ellipsis) goto skip_element;
2902 
2903  // Draw a text element.
2905  bool eol = false;
2906  if (t[t.size() - 1] == '\n') {
2907  eol = true;
2908  t = t.substr(0, t.size() - 1);
2909  }
2910  text.set_link_aware(false)
2911  .set_text(t, true);
2913  .set_font_size(item->font_size())
2914  .set_font_style(font::pango_text::STYLE_NORMAL)
2915  .set_alignment(PANGO_ALIGN_LEFT)
2917  .set_maximum_width(area.w)
2918  .set_maximum_height(area.h, false)
2919  .set_ellipse_mode(PANGO_ELLIPSIZE_END)
2921 
2922  surface s = text.render();
2923 
2924  // check if next element is text with almost no space to show it
2925  const int minimal_text = 12; // width in pixels
2926  config::const_child_iterator ee = elements.begin();
2927  if (!eol && rect.w - (x - rect.x + s->w) < minimal_text &&
2928  ++ee != elements.end() && !(*ee)["text"].empty())
2929  {
2930  // make this element longer to trigger rendering of ellipsis
2931  // (to indicate that next elements have not enough space)
2932  //NOTE this space should be longer than minimal_text pixels
2933  t = t + " ";
2934  text.set_text(t, true);
2935  s = text.render();
2936  // use the area of this element for next tooltips
2937  used_ellipsis = true;
2938  ellipsis_area.x = x;
2939  ellipsis_area.y = y;
2940  ellipsis_area.w = s->w;
2941  ellipsis_area.h = s->h;
2942  }
2943 
2944  screen_.blit_surface(x, y, s);
2945  area.w = s->w;
2946  area.h = s->h;
2947  if (area.h > tallest) {
2948  tallest = area.h;
2949  }
2950  if (eol) {
2951  x = rect.x;
2952  y += tallest;
2953  tallest = 0;
2954  } else {
2955  x += area.w;
2956  }
2957  }
2958  else if (!(t = elements.front()["image"].str()).empty())
2959  {
2960  if (used_ellipsis) goto skip_element;
2961 
2962  // Draw an image element.
2963  surface img(image::get_image(t));
2964 
2965  if (!img) {
2966  ERR_DP << "could not find image for report: '" << t << "'" << std::endl;
2967  continue;
2968  }
2969 
2970  if (area.w < img->w && image_count) {
2971  // We have more than one image, and this one doesn't fit.
2973  used_ellipsis = true;
2974  }
2975 
2976  if (img->w < area.w) area.w = img->w;
2977  if (img->h < area.h) area.h = img->h;
2978  draw_image_for_report(img, area);
2979 
2980  ++image_count;
2981  if (area.h > tallest) {
2982  tallest = area.h;
2983  }
2984 
2985  if (!used_ellipsis) {
2986  x += area.w;
2987  } else {
2988  ellipsis_area = area;
2989  }
2990  }
2991  else
2992  {
2993  // No text nor image, skip this element
2994  continue;
2995  }
2996 
2997  skip_element:
2998  t = elements.front()["tooltip"].t_str().c_str();
2999  if (!t.empty()) {
3000  if (!used_ellipsis) {
3001  tooltips::add_tooltip(area, t, elements.front()["help"].t_str().c_str());
3002  } else {
3003  // Collect all tooltips for the ellipsis.
3004  // TODO: need a better separator
3005  // TODO: assign an action
3006  ellipsis_tooltip << t;
3007  config::const_child_iterator ee = elements.begin();
3008  if (++ee != elements.end())
3009  ellipsis_tooltip << "\n _________\n\n";
3010  }
3011  }
3012  }
3013 
3014  if (used_ellipsis) {
3015  tooltips::add_tooltip(ellipsis_area, ellipsis_tooltip.str());
3016  }
3017 }
3018 
3020 {
3021  DBG_DP << "invalidate_all()\n";
3022  invalidateAll_ = true;
3023  invalidated_.clear();
3024 }
3025 
3027 {
3028  if(invalidateAll_)
3029  return false;
3030 
3031  bool tmp;
3032  tmp = invalidated_.insert(loc).second;
3033  return tmp;
3034 }
3035 
3036 bool display::invalidate(const std::set<map_location>& locs)
3037 {
3038  if(invalidateAll_)
3039  return false;
3040  bool ret = false;
3041  for (const map_location& loc : locs) {
3042  ret = invalidated_.insert(loc).second || ret;
3043  }
3044  return ret;
3045 }
3046 
3047 bool display::propagate_invalidation(const std::set<map_location>& locs)
3048 {
3049  if(invalidateAll_)
3050  return false;
3051 
3052  if(locs.size()<=1)
3053  return false; // propagation never needed
3054 
3055  bool result = false;
3056  {
3057  // search the first hex invalidated (if any)
3058  std::set<map_location>::const_iterator i = locs.begin();
3059  for(; i != locs.end() && invalidated_.count(*i) == 0 ; ++i) {}
3060 
3061  if (i != locs.end()) {
3062 
3063  // propagate invalidation
3064  // 'i' is already in, but I suspect that splitting the range is bad
3065  // especially because locs are often adjacents
3066  size_t previous_size = invalidated_.size();
3067  invalidated_.insert(locs.begin(), locs.end());
3068  result = previous_size < invalidated_.size();
3069  }
3070  }
3071  return result;
3072 }
3073 
3075 {
3077 }
3078 
3079 bool display::invalidate_locations_in_rect(const SDL_Rect& rect)
3080 {
3081  if(invalidateAll_)
3082  return false;
3083 
3084  bool result = false;
3085  for (const map_location &loc : hexes_under_rect(rect)) {
3086  result |= invalidate(loc);
3087  }
3088  return result;
3089 }
3090 
3092  if (get_map().is_village(loc)) {
3093  const int owner = dc_->village_owner(loc) - 1;
3094  if (owner >= 0 && flags_[owner].need_update()
3095  && (!fogged(loc) || !dc_->teams()[currentTeam_].is_enemy(owner+1))) {
3096  invalidate(loc);
3097  }
3098  }
3099 }
3100 
3102 {
3105  if (animate_map_) {
3106  for (const map_location &loc : get_visible_hexes())
3107  {
3108  if (shrouded(loc)) continue;
3109  if (builder_->update_animation(loc)) {
3110  invalidate(loc);
3111  } else {
3113  }
3114  }
3115  }
3116 
3117  for(const unit& u : dc_->units()) {
3118  u.anim_comp().refresh();
3119  }
3120  for (const unit* u : *fake_unit_man_) {
3121  u->anim_comp().refresh();
3122  }
3123 
3124 
3125  bool new_inval;
3126  do {
3127  new_inval = false;
3128  for (const unit & u : dc_->units()) {
3129  new_inval |= u.anim_comp().invalidate(*this);
3130  }
3131  for (const unit* u : *fake_unit_man_) {
3132  new_inval |= u->anim_comp().invalidate(*this);
3133  }
3134  } while (new_inval);
3135 }
3136 
3138 {
3139  for(const unit & u : dc_->units()) {
3140  u.anim_comp().set_standing();
3141  }
3142 }
3143 
3145 {
3146  const arrow_path_t & arrow_path = arrow.get_path();
3147  for (const map_location& loc : arrow_path)
3148  {
3149  arrows_map_[loc].push_back(&arrow);
3150  }
3151 }
3152 
3154 {
3155  const arrow_path_t & arrow_path = arrow.get_path();
3156  for (const map_location& loc : arrow_path)
3157  {
3158  arrows_map_[loc].remove(&arrow);
3159  }
3160 }
3161 
3163 {
3164  const arrow_path_t & previous_path = arrow.get_previous_path();
3165  for (const map_location& loc : previous_path)
3166  {
3167  arrows_map_[loc].remove(&arrow);
3168  }
3169  const arrow_path_t & arrow_path = arrow.get_path();
3170  for (const map_location& loc : arrow_path)
3171  {
3172  arrows_map_[loc].push_back(&arrow);
3173  }
3174 }
3175 
3177 {
3178  const SDL_Rect& rect = map_area();
3179  return pixel_position_to_hex(xpos_ + rect.x + rect.w / 2 , ypos_ + rect.y + rect.h / 2 );
3180 }
3181 
3182 void display::write(config& cfg) const
3183 {
3184  cfg["view_locked"] = view_locked_;
3185  cfg["color_adjust_red"] = color_adjust_.r;
3186  cfg["color_adjust_green"] = color_adjust_.g;
3187  cfg["color_adjust_blue"] = color_adjust_.b;
3188  get_middle_location().write(cfg.add_child("location"));
3189 }
3190 
3191 void display::read(const config& cfg)
3192 {
3193  view_locked_ = cfg["view_locked"].to_bool(false);
3194  color_adjust_.r = cfg["color_adjust_red"].to_int(0);
3195  color_adjust_.g = cfg["color_adjust_green"].to_int(0);
3196  color_adjust_.b = cfg["color_adjust_blue"].to_int(0);
3197 }
3198 
3200 {
3201  if (!reach_map_changed_) return;
3202  if (reach_map_.empty() != reach_map_old_.empty()) {
3203  // Invalidate everything except the non-darkened tiles
3204  reach_map &full = reach_map_.empty() ? reach_map_old_ : reach_map_;
3205 
3206  for (const auto& hex : get_visible_hexes()) {
3207  reach_map::iterator reach = full.find(hex);
3208  if (reach == full.end()) {
3209  // Location needs to be darkened or brightened
3210  invalidate(hex);
3211  } else if (reach->second != 1) {
3212  // Number needs to be displayed or cleared
3213  invalidate(hex);
3214  }
3215  }
3216  } else if (!reach_map_.empty()) {
3217  // Invalidate only changes
3218  reach_map::iterator reach, reach_old;
3219  for (reach = reach_map_.begin(); reach != reach_map_.end(); ++reach) {
3220  reach_old = reach_map_old_.find(reach->first);
3221  if (reach_old == reach_map_old_.end()) {
3222  invalidate(reach->first);
3223  } else {
3224  if (reach_old->second != reach->second) {
3225  invalidate(reach->first);
3226  }
3227  reach_map_old_.erase(reach_old);
3228  }
3229  }
3230  for (reach_old = reach_map_old_.begin(); reach_old != reach_map_old_.end(); ++reach_old) {
3231  invalidate(reach_old->first);
3232  }
3233  }
3235  reach_map_changed_ = false;
3236 }
3237 
3238 void display::handle_window_event(const SDL_Event& event) {
3239  if (event.type == SDL_WINDOWEVENT) {
3240  switch (event.window.event) {
3241  case SDL_WINDOWEVENT_RESIZED:
3242  case SDL_WINDOWEVENT_RESTORED:
3243  case SDL_WINDOWEVENT_EXPOSED:
3244  dirty_ = true;
3245 
3246  break;
3247  }
3248  }
3249 
3250 
3251 }
3252 
3253 void display::handle_event(const SDL_Event& event) {
3255  return;
3256  }
3257  if (event.type == DRAW_ALL_EVENT) {
3258  draw();
3259  }
3260 }
3261 
3262 display *display::singleton_ = nullptr;
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:832
static const std::array< drawing_layer, 4 > layer_groups
Definition: display.hpp:929
std::size_t font_size() const
Definition: theme.hpp:113
play_controller * controller
Definition: resources.cpp:22
TYPE
Used to specify the rendering format of images.
Definition: picture.hpp:229
void drawing_buffer_commit()
Draws the drawing_buffer_ and clears it.
Definition: display.cpp:1288
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number of the team that currently owns it...
void draw_minimap_units()
Definition: display.cpp:1874
surface get_image(const image::locator &i_locator, TYPE type)
Caches and returns an image.
Definition: picture.cpp:816
const std::string & get_modifications() const
Definition: picture.hpp:83
int zoom_index_
Definition: display.hpp:754
void raise_volatile_undraw_event()
Definition: events.cpp:814
void recalculate_shroud()
Definition: label.cpp:278
Reserve layers to be selected for WML.
Definition: display.hpp:833
virtual void pre_draw()
Called near the beginning of each draw() call.
Definition: display.hpp:674
bool discard_previous
An announcement according these options should replace the previous announce (typical of fast announc...
Definition: display.hpp:601
virtual SDL_Rect & location(const SDL_Rect &screen) const
Definition: theme.cpp:318
draw_layering(const bool auto_join=true)
Definition: video.cpp:55
const std::string & icon() const
Definition: theme.hpp:109
bool is_odd(T num)
Definition: math.hpp:36
virtual void select_hex(map_location hex)
Definition: display.cpp:1620
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:402
::tod_manager * tod_manager
Definition: resources.cpp:30
Arrows destined to be drawn on the map.
reports * reports_object_
Definition: display.hpp:771
unit_iterator end()
Definition: map.hpp:429
bool update_locked() const
Whether the screen has been &#39;locked&#39; or not.
Definition: video.cpp:328
bool minimap_draw_units()
Definition: general.cpp:815
void write(config &cfg) const
Definition: display.cpp:3182
virtual void handle_window_event(const SDL_Event &event)
Definition: display.cpp:3238
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:92
bool show_fps()
Definition: general.cpp:855
const team & get_team(int side) const
void remove_floating_label(int handle, int fadeout)
removes the floating label given by &#39;handle&#39; from the screen
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:296
Small struct to store and manipulate ToD color adjusts.
Definition: time_of_day.hpp:27
void adjust_color_overlay(int r, int g, int b)
Add r,g,b to the colors for all images displayed on the map.
Definition: display.cpp:470
const arrow_path_t & get_path() const
Definition: arrow.cpp:121
#define DefaultZoom
Definition: display.cpp:78
std::string image_mask
The image that is to be laid over all images while this time of day lasts.
Definition: time_of_day.hpp:96
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:475
void invalidate_animations()
Function to invalidate animated terrains and units which may have changed.
Definition: display.cpp:3101
#define ERR_DP
Definition: display.cpp:71
virtual void draw_sidebar()
Called near the end of a draw operation, derived classes can use this to render a specific sidebar...
Definition: display.hpp:717
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3026
color_t font_rgb() const
Definition: theme.hpp:114
static int hex_size()
Function which returns the size of a hex in pixels (from top tip to bottom tip or left edge to right ...
Definition: display.hpp:260
void draw_wrap(bool update, bool force)
Definition: display.cpp:1692
void flip()
Definition: display.cpp:1341
This class represents a single unit of a specific type.
Definition: unit.hpp:121
bool animate_water_
Local version of preferences::animate_water, used to detect when it&#39;s changed.
Definition: display.hpp:803
virtual void notify_observers()
tod_color color
The color modifications that should be made to the game board to reflect the time of day...
static void toggle_benchmark()
Toggle to continuously redraw the screen.
Definition: display.cpp:1331
CKey keys_
Definition: display.hpp:797
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:283
void set_clip_rect(const SDL_Rect &r)
bool set_zoom(bool increase)
Zooms the display in (true) or out (false).
Definition: display.cpp:2027
void change_display_context(const display_context *dc)
Definition: display.cpp:515
const map_location hex_clicked_on(int x, int y) const
given x,y co-ordinates of an onscreen pixel, will return the location of the hex that this pixel corr...
Definition: display.cpp:599
boost::circular_buffer< unsigned > frametimes_
Definition: display.hpp:776
bool rects_overlap(const SDL_Rect &rect1, const SDL_Rect &rect2)
Tests whether two rectangles overlap.
Definition: rect.cpp:34
int ypos_
Definition: display.hpp:746
bool reach_map_changed_
Definition: display.hpp:1040
static double get_zoom_factor()
Returns the current zoom factor.
Definition: display.hpp:263
drawing_buffer drawing_buffer_
Definition: display.hpp:978
void set_playing_team(std::size_t team)
set_playing_team sets the team whose turn it currently is
Definition: display.cpp:421
surface reverse_image(const surface &surf)
Horizontally flips an image.
Definition: picture.cpp:989
void draw_init()
Initiate a redraw.
Definition: display.cpp:1653
void set_idle_anim_rate(int rate)
Definition: display.cpp:2414
const map_location pixel_position_to_hex(int x, int y) const
given x,y co-ordinates of a pixel on the map, will return the location of the hex that this pixel cor...
Definition: display.cpp:614
void scroll_floating_labels(double xmove, double ymove)
moves all floating labels that have &#39;scroll_mode&#39; set to ANCHOR_LABEL_MAP
#define a
int draw_delay()
Definition: general.cpp:865
Definition: video.hpp:32
bool minimap_movement_coding()
Definition: general.cpp:795
std::map< std::string, SDL_Rect > reportRects_
Definition: display.hpp:783
void draw_all_panels()
redraw all panels associated with the map display
Definition: display.cpp:1475
void render_image(int x, int y, const display::drawing_layer drawing_layer, const map_location &loc, surface image, bool hreverse=false, bool greyscale=false, fixed_t alpha=ftofxp(1.0), color_t blendto={0, 0, 0}, double blend_ratio=0, double submerged=0.0, bool vreverse=false)
Draw an image at a certain location.
Definition: display.cpp:1552
bool in_dialog()
Definition: show_dialog.cpp:53
virtual void draw_invalidated()
Only called when there&#39;s actual redrawing to do.
Definition: display.cpp:2551
map_location mouseoverHex_
Definition: display.hpp:796
void flip()
Renders the screen.
Definition: video.cpp:308
void init_flags()
Init the flag list and the team colors used by ~TC.
Definition: display.cpp:308
Manages a list of fake units for the display object.
Mouseover overlay used by editor.
Definition: display.hpp:830
virtual void draw_hex(const map_location &hex)
Definition: arrow.cpp:137
child_itors child_range(config_key_type key)
Definition: config.cpp:344
static void draw_background(surface screen, const SDL_Rect &area, const std::string &image)
Definition: display.cpp:1496
std::string id
Definition: time_of_day.hpp:90
void lock_updates(bool value)
Stop the screen being redrawn.
Definition: video.cpp:319
const int SIZE_BUTTON_SMALL
Definition: constants.cpp:26
bool draw_coordinates_
Debug flag - overlay x,y coords on tiles.
Definition: display.hpp:1062
const SDL_Rect & map_area() const
Returns the area used for the map.
Definition: display.cpp:562
int scroll_speed()
Definition: general.cpp:751
void redraw_everything()
Invalidates entire screen, including all tiles and sidebar.
Definition: display.cpp:2419
void set_lifetime(int lifetime, int fadeout=100)
static lg::log_domain log_display("display")
int fps_handle_
Handle for the label which displays frames per second.
Definition: display.hpp:1049
pango_text & set_link_aware(bool b)
Definition: text.cpp:448
bool show_everything() const
Definition: display.hpp:94
The class terrain_builder is constructed from a config object, and a gamemap object.
Definition: builder.hpp:48
map_location minimap_location_on(int x, int y)
given x,y co-ordinates of the mouse, will return the location of the hex in the minimap that the mous...
Definition: display.cpp:760
void fill_surface_rect(surface &dst, SDL_Rect *dst_rect, const uint32_t color)
Fill a rectangle on a given surface.
Definition: rect.hpp:115
void reset_halo_manager()
Definition: display.cpp:521
int viewing_side() const
Definition: display.hpp:107
void set_tile_size(const unsigned int size)
Definition: general.cpp:630
reach_map reach_map_
Definition: display.hpp:1038
bool non_interactive() const
Definition: video.cpp:135
SDL_Rect intersect_rects(const SDL_Rect &rect1, const SDL_Rect &rect2)
Calculates the intersection of two rectangles.
Definition: rect.cpp:40
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:110
iterator & operator++()
increment y first, then when reaching bottom, increment x
Definition: display.cpp:666
std::vector< std::function< void(display &)> > redraw_observers_
Definition: display.hpp:1059
#define MaxZoom
Definition: display.cpp:81
map_location selectedHex_
Definition: display.hpp:795
void set_font_size(int font_size)
const std::vector< std::string > items
CVideo & screen_
Definition: display.hpp:737
surface scale_surface(const surface &surf, int w, int h)
Scale a surface using alpha-weighted modified bilinear filtering Note: causes artifacts with alpha gr...
Definition: utils.cpp:197
static display * singleton_
Definition: display.hpp:1080
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Draws a surface directly onto the screen framebuffer.
Definition: video.cpp:163
#define d
Belongs to a friendly side.
virtual void draw()
Draws invalidated items.
Definition: display.cpp:2478
surface blend_surface(const surface &surf, const double amount, const color_t color)
Blends a surface with a color.
Definition: utils.cpp:1717
bool animate_water()
Definition: general.cpp:790
const int SIZE_PLUS
Definition: constants.cpp:29
Top half part of grid image.
Definition: display.hpp:829
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:2158
void enable_menu(const std::string &item, bool enable)
Finds the menu which has a given item in it, and enables or disables it.
Definition: display.cpp:1762
Rectangular area of hexes, allowing to decide how the top and bottom edges handles the vertical shift...
Definition: display.hpp:306
An object to leave the synced context during draw or unsynced wml items when we don’t know whether w...
surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::map< map_location, unsigned int > *reach_map, bool ignore_terrain_disabled)
function to create the minimap for a given map the surface returned must be freed by the user ...
Definition: minimap.cpp:40
const std::vector< label > & labels() const
Definition: theme.hpp:254
static unsigned int zoom_
The current zoom, in pixels (on screen) per 72 pixels (in the graphic assets), i.e., 72 means 100%.
Definition: display.hpp:753
pango_text & get_text_renderer()
Returns a reference to a static pango_text object.
Definition: text.cpp:891
void clear_redraw_observers()
Clear the redraw observers.
Definition: display.cpp:2473
pango_text & set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
Definition: text.cpp:410
const SDL_Rect & minimap_area() const
mapx is the width of the portion of the display which shows the game area.
Definition: display.hpp:219
drawing_layer
The layers to render something on.
Definition: display.hpp:824
Same as SCALED_TO_HEX, but with Time of Day color tint applied.
Definition: picture.hpp:240
virtual const gamemap & map() const =0
std::vector< surface > get_fog_shroud_images(const map_location &loc, image::TYPE image_type)
Definition: display.cpp:968
void reset_standing_animations()
Definition: display.cpp:3137
void remove_arrow(arrow &)
Definition: display.cpp:3153
surface clone() const
Makes a copy of this surface.
Definition: surface.cpp:63
surface & getSurface()
Returns a reference to the framebuffer.
Definition: video.cpp:483
void add_frame(int duration, const T &value, bool force_change=false)
Adds a frame to an animation.
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
bool panelsDrawn_
Definition: display.hpp:766
#define DBG_DP
Definition: display.cpp:73
void draw_centered_on_background(surface surf, const SDL_Rect &rect, const color_t &color, surface target)
Definition: utils.cpp:2273
surface tod_hex_mask2
Definition: display.hpp:791
SDL_Rect get_non_transparent_portion(const surface &surf)
Definition: utils.cpp:2204
void init_flags_for_side_internal(std::size_t side, const std::string &side_color)
Definition: display.cpp:329
void update_display()
Copy the backbuffer to the framebuffer.
Definition: display.cpp:1364
std::vector< map_location > arrow_path_t
Definition: arrow.hpp:25
Unit and team statistics.
very simple iterator to walk into the rect_of_hexes
Definition: display.hpp:313
iterator begin() const
Definition: display.cpp:679
const rect_of_hexes get_visible_hexes() const
Returns the rectangular area of visible hexes.
Definition: display.hpp:343
surface submerge_alpha(const surface &surf, int depth, float alpha_base, float alpha_delta)
Progressively reduce alpha of bottom part of the surface.
Definition: utils.cpp:1249
pango_text & set_alignment(const PangoAlignment alignment)
Definition: text.cpp:424
std::vector< std::shared_ptr< gui::button > > action_buttons_
Definition: display.hpp:786
map_location loc_
surface map_screenshot_surf_
Definition: display.hpp:1057
void clear_tooltips()
Definition: tooltips.cpp:121
virtual bool in_editor() const
Definition: display.hpp:207
bool team_valid() const
Definition: display.cpp:735
#define b
const theme::action * action_pressed()
Definition: display.cpp:1730
const std::string & text() const
Definition: theme.hpp:107
std::vector< std::string > shroud_images_
Definition: display.hpp:793
void draw_image_for_report(surface &img, SDL_Rect &rect)
Definition: display.cpp:2768
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:56
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1011
void blindfold(bool flag)
Definition: display.cpp:531
void parse_team_overlays()
Check the overlay_map for proper team-specific overlays to be displayed/hidden.
Definition: display.cpp:117
const config & options()
Definition: game.cpp:569
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
Belongs to a non-friendly side; normally visualised by not displaying an orb.
surface flop_surface(const surface &surf)
Definition: utils.cpp:1981
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
const std::string & id() const
Gets this unit&#39;s id.
Definition: unit.hpp:371
uint32_t last_frame_finished_
Definition: display.hpp:780
void adjust_surface_alpha(surface &surf, fixed_t amount)
Definition: utils.cpp:1087
void get_terrain_images(const map_location &loc, const std::string &timeid, TERRAIN_TYPE terrain_type)
Definition: display.cpp:1055
const arrow_path_t & get_previous_path() const
Definition: arrow.cpp:126
double turbo_speed() const
Definition: display.cpp:2400
static bool zoom_at_max()
Definition: display.cpp:2017
void draw_minimap()
Definition: display.cpp:1796
light_string get_light_string(int op, int r, int g, int b)
Returns the light_string for one light operation.
Definition: picture.cpp:593
const std::string & prefix() const
Definition: theme.hpp:131
void new_animation_frame()
Definition: animated.cpp:31
static unsigned calculate_fps(unsigned frametime)
Definition: display.cpp:1359
std::string label
What to show in the filter&#39;s drop-down list.
Definition: manager.cpp:217
static bool outside_area(const SDL_Rect &area, const int x, const int y)
Check if the bbox of the hex at x,y has pixels outside the area rectangle.
Definition: display.cpp:590
bool tile_fully_on_screen(const map_location &loc) const
Check if a tile is fully visible on screen.
Definition: display.cpp:2107
const theme::menu * menu_pressed()
Definition: display.cpp:1746
int w() const
Effective map width.
Definition: map.hpp:50
Arrows destined to be drawn on the map.
Definition: arrow.hpp:30
surface screenshot(bool map_screenshot=false)
Capture a (map-)screenshot into a surface.
Definition: display.cpp:790
std::string theme()
Definition: game.cpp:815
std::string get_orb_color(orb_status os)
Wrapper for the various preferences::unmoved_color(), moved_color(), etc methods, using the enum inst...
Definition: orb_status.cpp:40
bool font_rgb_set() const
Definition: theme.hpp:115
std::string get_user_data_dir()
Definition: filesystem.cpp:792
bool invalidate_visible_locations_in_rect(const SDL_Rect &rect)
Definition: display.cpp:3074
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.hpp:619
void scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type, bool force=true)
Definition: display.cpp:2124
config & add_child_at(config_key_type key, const config &val, unsigned index)
Definition: config.cpp:546
bool valid() const
Definition: location.hpp:89
bool map_screenshot_
Used to indicate to drawing functions that we are doing a map screenshot.
Definition: display.hpp:1023
arrows_map_t arrows_map_
Maps the list of arrows for each location.
Definition: display.hpp:1071
virtual image::TYPE get_image_type(const map_location &loc)
Definition: display.cpp:2760
bool idle_anim() const
Definition: display.hpp:497
const std::unique_ptr< map_labels > map_labels_
Definition: display.hpp:770
int xpos_
Position of the top-left corner of the viewport, in pixels.
Definition: display.hpp:746
config generate_report(const std::string &name, context &ct, bool only_static=false)
Definition: reports.cpp:1759
pango_text & set_font_size(const unsigned font_size)
Definition: text.cpp:331
void update_tod(const time_of_day *tod_override=nullptr)
Applies r,g,b coloring to the map.
Definition: display.cpp:459
void render_buttons()
Definition: display.cpp:938
bool dirty_
Definition: display.hpp:1075
std::string background_image
Definition: theme.hpp:91
bool idle_anim_
Definition: display.hpp:1054
SDL_Rect pango_draw_text(CVideo *gui, const SDL_Rect &area, int size, const color_t &color, const std::string &text, int x, int y, bool use_tooltips, pango_text::FONT_STYLE style)
Draws text on the screen.
SDL_Rect minimap_location_
Definition: display.hpp:760
pango_text & set_characters_per_line(const unsigned characters_per_line)
Definition: text.cpp:379
double size
Definition: theme.hpp:89
void set_zoom(unsigned int amount)
Sets the scaling factor for images.
Definition: picture.cpp:705
TERRAIN_TYPE
Used as a parameter for the get_terrain_at function.
Definition: builder.hpp:52
display(const display_context *dc, std::weak_ptr< wb::manager > wb, reports &reports_object, const config &theme_cfg, const config &level, bool auto_join=true)
Definition: display.cpp:168
std::string grid_bottom
tod_color color_adjust_
Definition: display.hpp:1073
void reinit_flags_for_team(const team &)
Rebuild the flag list (not team colors) for a single side.
Definition: display.cpp:324
fake_unit_manager * fake_units
Definition: resources.cpp:31
bool is_enemy(int n) const
Definition: team.hpp:255
void draw_text_in_hex(const map_location &loc, const drawing_layer layer, const std::string &text, std::size_t font_size, color_t color, double x_in_hex=0.5, double y_in_hex=0.5)
Draw text on a hex.
Definition: display.cpp:1523
void bounds_check_position()
Definition: display.cpp:2356
virtual void post_draw()
Called at the very end of each draw() call.
Definition: display.hpp:681
Modify, read and display user preferences.
bool font_rgb_set() const
Definition: theme.hpp:139
bool invalidateGameStatus_
Definition: display.hpp:769
#define MinZoom
Definition: display.cpp:80
void set_position(double xpos, double ypos)
void update_arrow(arrow &a)
Called by arrow objects when they change.
Definition: display.cpp:3162
map_display and display: classes which take care of displaying the map and game-data on the screen...
unsigned int fps_actual_
Definition: display.hpp:779
virtual const unit_map & units() const =0
void add_overlay(const map_location &loc, const std::string &image, const std::string &halo="", const std::string &team_name="", const std::string &item_id="", bool visible_under_fog=true, float z_order=0)
Functions to add and remove overlays from locations.
Definition: display.cpp:136
bool animate_map()
Definition: general.cpp:785
std::string shroud_prefix
Definition: game_config.cpp:72
void create_buttons()
Definition: display.cpp:879
const std::string & get_id() const
Definition: theme.hpp:53
const color_t YELLOW_COLOR
static const std::string & get_direction(std::size_t n)
Definition: display.cpp:962
const rect_of_hexes hexes_under_rect(const SDL_Rect &r) const
Return the rectangular area of hexes overlapped by r (r is in screen coordinates) ...
Definition: display.cpp:688
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:3019
void layout_buttons()
Definition: display.cpp:852
int invalidated_hexes_
Count work done for the debug info displayed under fps.
Definition: display.hpp:1051
bool draw_terrain_codes_
Debug flag - overlay terrain codes on tiles.
Definition: display.hpp:1064
int32_t fixed_t
Definition: math.hpp:313
void pump()
Definition: events.cpp:473
const color_t NORMAL_COLOR
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:745
pango_text & set_family_class(font::family_class fclass)
Definition: text.cpp:320
Generic locator abstracting the location of an image.
Definition: picture.hpp:60
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:23
static int hex_width()
Function which returns the width of a hex in pixels, up to where the next hex starts.
Definition: display.hpp:254
lu_byte right
Definition: lparser.cpp:1227
std::string flag_rgb
static unsigned int last_zoom_
The previous value of zoom_.
Definition: display.hpp:756
Fog and shroud.
Definition: display.hpp:853
void set_color_adjustment(int r, int g, int b)
Changes Time of Day color tint for all applicable image types.
Definition: picture.cpp:691
std::unique_ptr< halo::manager > halo_man_
Definition: display.hpp:661
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
Definition: math.hpp:318
int idle_anim_rate()
Definition: general.cpp:485
Encapsulates the map of the game.
Definition: location.hpp:38
void set_color(const color_t &color)
void set_team(const team *)
Definition: label.cpp:139
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.cpp:740
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:206
#define DRAW_ALL_EVENT
Definition: events.hpp:30
unit_iterator find(std::size_t id)
Definition: map.cpp:310
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:40
void process_reachmap_changes()
Definition: display.cpp:3199
bool faked() const
Definition: video.hpp:66
const border_t & border() const
Definition: theme.hpp:284
A button is a control that can be pushed to start an action or close a dialog.
Definition: button.hpp:50
Definition: video.cpp:51
void recalculate_labels()
Definition: label.cpp:245
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
virtual void handle_event(const SDL_Event &)
Definition: display.cpp:3253
surface & render(const SDL_Rect &viewport)
Returns the rendered text.
Definition: text.cpp:94
surface & get_screen_surface()
return the screen surface or the surface used for map_screenshot.
Definition: display.hpp:204
surface tod_hex_mask1
Definition: display.hpp:791
const color_t BLACK_COLOR
virtual overlay_map & get_overlays()=0
std::size_t i
Definition: function.cpp:967
virtual const std::vector< team > & teams() const =0
int current_refresh_rate() const
Definition: video.hpp:165
bool is_blindfolded() const
Definition: display.cpp:539
bool show_border
Definition: theme.hpp:94
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:2203
void announce(const std::string &msg, const color_t &color=font::GOOD_COLOR, const announce_options &options=announce_options())
Announce a message prominently.
Definition: display.cpp:1780
virtual void highlight_hex(map_location hex)
Definition: display.cpp:1628
Holds options for calls to function &#39;announce&#39; (announce).
Definition: display.hpp:591
default layer for drawing moving units
Definition: display.hpp:844
std::size_t activeTeam_
Definition: display.hpp:895
const std::string & postfix() const
Definition: theme.hpp:132
std::vector< std::shared_ptr< gui::button > > menu_buttons_
Definition: display.hpp:786
unsigned int tile_size()
Definition: general.cpp:625
void set_diagnostic(const std::string &msg)
Definition: display.cpp:1635
void scroll_to_tiles(map_location loc1, map_location loc2, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, double add_spacing=0.0, bool force=true)
Scroll such that location loc1 is on-screen.
Definition: display.cpp:2215
void invalidate_animations_location(const map_location &loc)
Per-location invalidation called by invalidate_animations() Extra game per-location invalidation (vil...
Definition: display.cpp:3091
default layer for drawing units
Definition: display.hpp:835
map_location get_middle_location() const
Definition: display.cpp:3176
static map_location::DIRECTION s
std::vector< std::string > names
Definition: build_info.cpp:66
unsigned int fps_counter_
Definition: display.hpp:777
virtual ~display()
Definition: display.cpp:287
double g
Definition: astarsearch.cpp:65
color_t font_rgb() const
Definition: theme.hpp:138
std::size_t playing_team() const
The playing team is the team whose turn it is.
Definition: display.hpp:101
bool draw_num_of_bitmaps_
Debug flag - overlay number of bitmaps on tiles.
Definition: display.hpp:1066
bool redraw_background_
Definition: display.hpp:762
void read(const config &cfg)
Definition: display.cpp:3191
bool dont_show_all_
Definition: display.hpp:739
int get_location_y(const map_location &loc) const
Definition: display.cpp:755
std::basic_string< signed char > light_string
Type used to store color information of central and adjacent hexes.
Definition: picture.hpp:176
const SDL_Rect & map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.hpp:242
void start_animation(int start_time, bool cycles=false)
Starts an animation cycle.
Helper structure for rendering the terrains.
Definition: display.hpp:938
void undraw_floating_labels(surface screen)
void raise_volatile_draw_event()
Definition: events.cpp:796
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
const std::string & image() const
Definition: theme.hpp:156
bool tile_nearly_on_screen(const map_location &loc) const
Checks if location loc or one of the adjacent tiles is visible on screen.
Definition: display.cpp:2114
bool invalidate_locations_in_rect(const SDL_Rect &rect)
invalidate all hexes under the rectangle rect (in screen coordinates)
Definition: display.cpp:3079
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
Layer for the terrain drawn in front of the unit.
Definition: display.hpp:836
bool add_exclusive_draw(const map_location &loc, unit &unit)
Allows a unit to request to be the only one drawn in its hex.
Definition: display.cpp:428
virtual void draw_hex(const map_location &loc)
Redraws a single gamemap location.
Definition: display.cpp:2588
Declarations for File-IO.
Represents terrains which are to be drawn in front of them.
Definition: builder.hpp:57
bool is_zero() const
Definition: time_of_day.hpp:36
static bool zoom_at_min()
Definition: display.cpp:2022
int w
const menu * get_menu_item(const std::string &key) const
Definition: theme.cpp:949
Used for the bottom half part of grid image.
Definition: display.hpp:840
theme theme_
Definition: display.hpp:748
virtual const SDL_Rect & get_clip_rect()
Get the clipping rectangle for drawing.
Definition: display.cpp:2546
const bool & debug
bool view_locked_
Definition: display.hpp:747
surface mouseover_hex_overlay_
Definition: display.hpp:788
const std::vector< action > & actions() const
Definition: theme.hpp:257
const std::string & get_filename() const
Definition: picture.hpp:78
Definitions for the terrain builder.
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
surface greyscale_image(const surface &surf)
Definition: utils.cpp:641
Represents terrains which are to be drawn behind unit sprites.
Definition: builder.hpp:53
static gui::button::TYPE string_to_button_type(const std::string &type)
Definition: display.cpp:952
Layer for the terrain drawn behind the unit.
Definition: display.hpp:825
static int get_zoom_levels_index(unsigned int zoom_level)
Definition: display.cpp:97
config & add_child(config_key_type key)
Definition: config.cpp:514
drawing_buffer_key(const map_location &loc, drawing_layer layer)
Definition: display.cpp:1259
std::vector< std::string > fog_images_
Definition: display.hpp:792
Text class.
Definition: text.hpp:75
std::chrono::seconds fps_start_
Definition: display.hpp:778
std::vector< animated< image::locator > > imagelist
A shorthand typedef for a list of animated image locators, the base data type returned by the get_ter...
Definition: builder.hpp:75
void set_team(std::size_t team, bool observe=false)
Sets the team controlled by the player using the computer.
Definition: display.cpp:402
surface get_lighted_image(const image::locator &i_locator, const light_string &ls, TYPE type)
Caches and returns an image with a lightmap applied to it.
Definition: picture.cpp:889
const status_item * get_status_item(const std::string &item) const
Definition: theme.cpp:908
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an SDL_Rect with the given dimensions.
Definition: rect.hpp:40
std::map< map_location, unsigned int > reach_map
Definition: display.hpp:1037
bool redrawMinimap_
Definition: display.hpp:761
bool set_resolution(const SDL_Rect &screen)
Definition: theme.cpp:604
Definition: display.hpp:45
void drawing_buffer_add(const drawing_layer layer, const map_location &loc, int x, int y, const surface &surf, const SDL_Rect &clip=SDL_Rect())
Add an item to the drawing buffer.
Definition: display.cpp:1209
const std::vector< team > & get_teams() const
Definition: display.hpp:98
static color_t get_minimap_color(int side)
Definition: team.cpp:966
reach_map reach_map_old_
Definition: display.hpp:1039
int get_location_x(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition: display.cpp:750
const gamemap & get_map() const
Definition: display.hpp:96
Image rescaled to fit into a hexagonal tile according to the zoom settings.
Definition: picture.hpp:238
const std::string & color() const
Definition: team.hpp:268
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
virtual void post_commit()
Hook for actions to take right after draw() calls drawing_buffer_commit No action here by default...
Definition: display.hpp:701
exclusive_unit_draw_requests_t exclusive_unit_draw_requests_
map of hexes where only one unit should be drawn, the one identified by the associated id string ...
Definition: display.hpp:666
bool invalidateAll_
Definition: display.hpp:763
void redraw_unit(const unit &u) const
draw a unit.
Definition: drawer.cpp:92
events::generic_event scroll_event_
Event raised when the map is being scrolled.
Definition: display.hpp:774
iterator end() const
Definition: display.cpp:683
#define f
int lifetime
Lifetime measured in milliseconds.
Definition: display.hpp:594
double t
Definition: astarsearch.cpp:65
void reload_map()
Updates internals that cache map size.
Definition: display.cpp:509
uint32_t to_argb_bytes() const
Returns the stored color as a uint32_t, an ARGB format.
Definition: color.hpp:146
color_t rep() const
High-contrast shade, intended for the minimap markers.
Definition: color_range.hpp:95
orb_status unit_orb_status(const unit &u) const
Returns an enumurated summary of whether this unit can move and/or attack.
std::string tile_image
Definition: theme.hpp:92
constexpr const SDL_Rect empty_rect
Definition: rect.hpp:32
std::map< std::string, surface > reportSurfaces_
Definition: display.hpp:784
std::set< map_location > invalidated_
Definition: display.hpp:787
std::vector< std::string > split(const config_attribute_value &val)
unsigned int tile_size
Definition: game_config.cpp:69
void toggle_default_zoom()
Sets the zoom amount to the default.
Definition: display.cpp:2095
lu_byte left
Definition: lparser.cpp:1226
void drawing_buffer_clear()
Clears the drawing buffer.
Definition: display.cpp:1326
std::map< std::string, config > reports_
Definition: display.hpp:785
const display_context * dc_
Definition: display.hpp:660
surface brighten_image(const surface &surf, fixed_t amount)
Definition: utils.cpp:1045
std::size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:106
surface tile_surface(const surface &surf, int w, int h, bool centered)
Tile a surface.
Definition: utils.cpp:553
const color_range & color_info(const std::string &name)
std::vector< surface > terrain_image_vector_
Definition: display.hpp:815
Functions to load and save images from/to disk.
double idle_anim_rate_
Definition: display.hpp:1055
void fill_rectangle(const SDL_Rect &rect, const color_t &color)
Draws a filled rectangle.
Definition: rect.cpp:66
int drawn_hexes_
Definition: display.hpp:1052
Standard logging facilities (interface).
double turbo_speed_
Definition: display.hpp:767
int diagnostic_label_
Definition: display.hpp:765
void add_redraw_observer(std::function< void(display &)> f)
Adds a redraw observer, a function object to be called when redraw_everything is used.
Definition: display.cpp:2468
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:201
surface pango_render_text(const std::string &text, int size, const color_t &color, font::pango_text::FONT_STYLE style, bool use_markup, int max_width)
Returns a SDL surface containing the rendered text.
std::string ellipsis
std::size_t font_size() const
Definition: theme.hpp:137
EXIT_STATUS start(const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Definition: editor_main.cpp:30
static void draw_label(CVideo &video, surface target, const theme::label &label)
Definition: display.cpp:1448
bool scroll_to_action()
Definition: general.cpp:383
A RAII object to temporary leave the synced context like in wesnoth.synchronize_choice.
const int SIZE_FLOAT_LABEL
Definition: constants.cpp:32
map_labels & labels()
Definition: display.cpp:2536
const std::vector< menu > & menus() const
Definition: theme.hpp:255
#define e
static void fill_images_list(const std::string &prefix, std::vector< std::string > &images)
Definition: display.cpp:476
const std::unique_ptr< fake_unit_manager > fake_unit_man_
Definition: display.hpp:757
#define SmallZoom
Definition: display.cpp:79
#define final_zoom_index
Definition: display.cpp:77
std::vector< animated< image::locator > > flags_
Animated flags for each team.
Definition: display.hpp:811
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:465
pango_text & set_maximum_height(int height, bool multiline)
Definition: text.cpp:391
#define zoom_levels
Definition: display.cpp:76
int get_end_time() const
std::string team_name
Definition: overlay.hpp:54
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:32
static void draw_panel(CVideo &video, const theme::panel &panel, std::vector< std::shared_ptr< gui::button >> &)
Definition: display.cpp:1427
int side() const
Definition: team.hpp:200
virtual const time_of_day & get_time_of_day(const map_location &loc=map_location::null_location()) const
Definition: display.cpp:453
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
std::vector< std::tuple< int, int, int > > fps_history_
Definition: display.hpp:1077
mock_char c
void remove_single_overlay(const map_location &loc, const std::string &toDelete)
remove_single_overlay will remove a single overlay from a tile
Definition: display.cpp:156
SDL_Rect screen_area(bool as_pixels=true) const
Returns the current window renderer area, either in pixels or screen coordinates. ...
Definition: video.cpp:271
surface get_flag(const map_location &loc)
Definition: display.cpp:382
pango_text & set_foreground_color(const color_t &color)
Definition: text.cpp:354
static map_location::DIRECTION n
std::shared_ptr< halo_record > handle
Definition: halo.hpp:30
int h() const
Effective map height.
Definition: map.hpp:53
int add_tooltip(const SDL_Rect &rect, const std::string &message, const std::string &action, bool use_markup, const surface &foreground)
Definition: tooltips.cpp:176
bool scroll(int xmov, int ymov, bool force=false)
Scrolls the display by xmov,ymov pixels.
Definition: display.cpp:1926
bool turbo_
Definition: display.hpp:768
void add_arrow(arrow &)
Definition: display.cpp:3144
void remove_overlay(const map_location &loc)
remove_overlay will remove all overlays on a tile.
Definition: display.cpp:151
bool animate_map_
Local cache for preferences::animate_map, since it is constantly queried.
Definition: display.hpp:800
Transitional API for porting SDL_ttf-based code to Pango.
const SDL_Rect & max_map_area() const
Returns the maximum area used for the map regardless to resolution and view size. ...
Definition: display.cpp:545
static rng & default_instance()
Definition: random.cpp:74
#define LOG_DP
Definition: display.cpp:72
const std::vector< panel > & panels() const
Definition: theme.hpp:253
static void toggle_debug_foreground()
Toggle to debug foreground terrain.
Definition: display.cpp:1336
std::string grid_top
const std::unique_ptr< terrain_builder > builder_
Definition: display.hpp:758
static const std::string & get_variant(const std::vector< std::string > &variants, const map_location &loc)
Definition: display.cpp:498
void invalidate_theme()
Definition: display.hpp:407
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::string fog_prefix
Definition: game_config.cpp:72
bool empty() const
Definition: config.cpp:941
void refresh_report(const std::string &report_name, const config *new_cfg=nullptr)
Redraws the specified report (if anything has changed).
Definition: display.cpp:2806
std::weak_ptr< wb::manager > wb_
Definition: display.hpp:662
std::shared_ptr< gui::button > find_menu_button(const std::string &id)
Definition: display.cpp:842
Definition: display.hpp:49
void set_theme(config theme_cfg)
Definition: display.cpp:297
int blindfold_ctr_
Definition: display.hpp:656
const std::string & team_name() const
Definition: team.hpp:309
void draw_floating_labels(surface screen)
TERRAIN_TYPE
Definition: display.hpp:721
bool grid_
Definition: display.hpp:764
std::vector< std::string > square_parenthetical_split(const std::string &val, const char separator, const std::string &left, const std::string &right, const int flags)
Similar to parenthetical_split, but also expands embedded square brackets.
std::string remove_exclusive_draw(const map_location &loc)
Cancels an exclusive draw request.
Definition: display.cpp:441
const int SIZE_SMALL
Definition: constants.cpp:24
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:410
const color_t BAD_COLOR
"black stripes" on unreachable hexes.
Definition: display.hpp:851
std::size_t currentTeam_
Definition: display.hpp:738
void rebuild_all()
Rebuild all dynamic terrain.
Definition: display.cpp:504
bool propagate_invalidation(const std::set< map_location > &locs)
If this set is partially invalidated, invalidate all its hexes.
Definition: display.cpp:3047
void write(config &cfg) const
Definition: location.cpp:212
surface minimap_
Definition: display.hpp:759