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