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