The Battle for Wesnoth  1.19.25+dev
game_display.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2025
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * During a game, show map & info-panels at top+right.
19  */
20 
21 #include "game_display.hpp"
22 
23 #include <utility>
24 
25 
26 #include "cursor.hpp"
27 #include "display_chat_manager.hpp"
28 #include "fake_unit_manager.hpp"
29 #include "floating_label.hpp"
30 #include "game_board.hpp"
32 #include "log.hpp"
33 #include "map/map.hpp"
34 #include "font/standard_colors.hpp"
35 #include "reports.hpp"
36 #include "resources.hpp"
37 #include "tod_manager.hpp"
38 #include "color.hpp"
39 #include "synced_context.hpp"
40 #include "units/unit.hpp"
41 #include "units/drawer.hpp"
42 #include "units/types.hpp"
43 #include "utils/general.hpp"
44 #include "whiteboard/manager.hpp"
45 #include "overlay.hpp"
46 #include "draw.hpp"
47 
48 static lg::log_domain log_display("display");
49 #define ERR_DP LOG_STREAM(err, log_display)
50 #define LOG_DP LOG_STREAM(info, log_display)
51 #define DBG_DP LOG_STREAM(debug, log_display)
52 
53 static lg::log_domain log_engine("engine");
54 #define ERR_NG LOG_STREAM(err, log_engine)
55 
56 
58  std::weak_ptr<wb::manager> wb,
59  reports& reports_object,
60  const std::string& theme_id,
61  const config& level)
62  : display(&board, std::move(wb), reports_object, theme_id, level)
63  , overlay_map_()
64  , attack_indicator_src_()
65  , attack_indicator_dst_()
66  , route_()
67  , displayedUnitHex_()
68  , first_turn_(true)
69  , in_game_(false)
70  , chat_man_(new display_chat_manager(*this))
71  , mode_(RUNNING)
72  , needs_rebuild_(false)
73  , flash_fade_route_()
74  , flash_fade_route_start_time_()
75  , flash_fade_route_active_(false)
76 {
77 }
78 
80 {
82  return;
83  }
84 
85  auto now = std::chrono::steady_clock::now();
86  auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - flash_fade_route_start_time_);
87 
88  // Clean expired footpath
89  if(elapsed >= FLASH_FADE_ROUTE_TOTAL_DURATION) {
90  for(const auto& step : flash_fade_route_.steps) {
91  invalidate(step);
92  }
93 
95  flash_fade_route_.steps.clear();
96  flash_fade_route_.marks.clear();
97  return;
98  }
99 
100  // Trigger redraw of active path
101  for(const auto& step : flash_fade_route_.steps) {
102  invalidate(step);
103  }
104 }
105 
107 {
108  // Clear existing flash fading footprints from the screen
110  for(const auto& step : flash_fade_route_.steps) {
111  invalidate(step);
112  }
113  }
114 
115  // Reset the fading route data
116  flash_fade_route_.steps = remaining_route.steps;
117  flash_fade_route_.marks = remaining_route.marks;
118 
119  // Start the animation cycle
120  if(!flash_fade_route_.steps.empty()) {
122  flash_fade_route_start_time_ = std::chrono::steady_clock::now();
123 
124  // Trigger a redraw for all hexes in the new route
125  for(const auto& step : flash_fade_route_.steps) {
126  invalidate(step);
127  }
128  }
129 }
130 
132 {
133  try {
134  chat_man_->prune_chat_messages(true);
135  } catch(...) {
136  DBG_DP << "Caught exception in game_display destructor: " << utils::get_unknown_exception_type();
137  }
138 }
139 
141 {
142  if(!first_turn_) {
145 
146  if(old_tod.image_mask != tod.image_mask) {
147  fade_tod_mask(old_tod.image_mask, tod.image_mask);
148  }
149  }
150 
151  first_turn_ = false;
152 
153  update_tod();
154 }
155 
157 {
158  if(hex.valid() && fogged(hex)) {
159  return;
160  }
161  display::select_hex(hex);
162 
163  display_unit_hex(hex);
164 }
165 
167 {
168  wb::future_map_if future(!synced_context::is_synced()); /**< Lasts for whole method. */
169 
171  if (u) {
172  displayedUnitHex_ = hex;
173  invalidate_unit();
174  } else {
176  if (u) {
177  // mouse moved from unit hex to non-unit hex
178  if (context().units().count(selectedHex_)) {
180  invalidate_unit();
181  }
182  }
183  }
184 
187 }
188 
189 
191 {
192  if (!hex.valid())
193  return;
194 
195  wb::future_map_if future(!synced_context::is_synced()); /**< Lasts for whole method. */
196 
198  if (u) {
199  displayedUnitHex_ = hex;
200  invalidate_unit();
201  }
202 }
203 
205 {
206  if (src == displayedUnitHex_) {
208  invalidate_unit();
209  }
210 }
211 
212 void game_display::scroll_to_leader(int side, SCROLL_TYPE scroll_type,bool force)
213 {
215 
216  if(leader.valid() && leader->is_visible_to_team(viewing_team(), false)) {
217  scroll_to_tile(leader->get_location(), scroll_type, true, force);
218  }
219 }
220 
222 {
223  display::update();
224 
225  if (std::shared_ptr<wb::manager> w = wb_.lock()) {
226  w->pre_draw();
228  }
230  /**
231  * @todo FIXME: must modify changed, but best to do it at the
232  * floating_label level
233  */
234  chat_man_->prune_chat_messages();
235 }
236 
237 
239 {
240  display::render();
241 
242  if (std::shared_ptr<wb::manager> w = wb_.lock()) {
243  w->post_draw();
244  }
245 }
246 
248 {
250  if (fake_unit_man_->empty()) {
251  return;
252  }
253  unit_drawer drawer = unit_drawer(*this);
254 
255  for(const unit* temp_unit : *fake_unit_man_) {
256  const map_location& loc = temp_unit->get_location();
257  if(utils::contains(invalidated_, loc) && unit_can_draw_here(loc, *temp_unit)) {
258  drawer.redraw_unit(*temp_unit);
259  }
260  }
261 }
262 
263 namespace
264 {
265 const image::locator mouseover_normal_top
266  {"misc/hover-hex-top.png", "~RC(magenta>gold)"};
267 const image::locator mouseover_normal_bot
268  {"misc/hover-hex-bottom.png", "~RC(magenta>gold)"};
269 
270 const image::locator mouseover_enemy_top
271  {"misc/hover-hex-enemy-top.png", "~RC(magenta>red)"};
272 const image::locator mouseover_enemy_bot
273  {"misc/hover-hex-enemy-bottom.png", "~RC(magenta>red)"};
274 
275 const image::locator mouseover_self_top
276  {"misc/hover-hex-top.png", "~RC(magenta>green)"};
277 const image::locator mouseover_self_bot
278  {"misc/hover-hex-bottom.png", "~RC(magenta>green)"};
279 
280 const image::locator mouseover_ally_top
281  {"misc/hover-hex-top.png", "~RC(magenta>lightblue)"};
282 const image::locator mouseover_ally_bot
283  {"misc/hover-hex-bottom.png", "~RC(magenta>lightblue)"};
284 
285 /**
286  * Function to return 2 half-hex footsteps images for the given location.
287  * Only loc is on the current route set by set_route.
288  */
289 std::vector<texture> footsteps_images(const map_location& loc, const pathfind::marked_route& route, const display_context* dc)
290 {
291  std::vector<texture> res;
292 
293  if (route.steps.size() < 2) {
294  return res; // no real "route"
295  }
296 
297  std::vector<map_location>::const_iterator i =
298  std::find(route.steps.begin(),route.steps.end(),loc);
299 
300  if( i == route.steps.end()) {
301  return res; // not on the route
302  }
303 
304  // Determine which set of footprint images the unit uses
305  std::string footprints;
306  int move_cost = 1;
307  const unit_map::const_iterator u = dc->units().find(route.steps.front());
308  if(u != dc->units().end()) {
309  move_cost = u->movement_cost(dc->map().get_terrain(loc));
310  footprints = u->type().footprints();
311  }
312 
313  if(footprints.empty()) {
314  footprints = "humanoid";
315  }
316  const std::string foot_speed_prefix = "footsteps/" + footprints + "/";
317 
318  // Generate a red tint string based on movement cost.
319  std::string color_mod;
320  if(move_cost > 1) {
321  int reduction = std::min(255, (move_cost - 1) * 85);
322  color_mod = "~CS(0,-" + std::to_string(reduction) + ",-" + std::to_string(reduction) + ")";
323  }
324 
325  texture teleport;
326 
327  // We draw 2 half-hex (with possibly different directions),
328  // but skip the first for the first step.
329  const int first_half = (i == route.steps.begin()) ? 1 : 0;
330  // and the second for the last step
331  const int second_half = (i+1 == route.steps.end()) ? 0 : 1;
332 
333  for (int h = first_half; h <= second_half; ++h) {
334  const std::string sense( h==0 ? "-in" : "-out" );
335 
336  if (!tiles_adjacent(*(i+(h-1)), *(i+h))) {
337  std::string teleport_image =
339  teleport = image::get_texture(teleport_image, image::HEXED);
340  continue;
341  }
342 
343  // In function of the half, use the incoming or outgoing direction
344  map_location::direction dir = (i+(h-1))->get_relative_dir(*(i+h));
345 
346  std::string rotate;
348  // No image, take the opposite direction and do a 180 rotation
349  dir = i->get_opposite_direction(dir);
350  rotate = "~FL(horiz)~FL(vert)";
351  }
352 
353  const std::string image = foot_speed_prefix + "footprint"
354  + sense + "-" + i->write_direction(dir)
355  + ".png" + rotate + color_mod;
356 
357  res.push_back(image::get_texture(image, image::HEXED));
358  }
359 
360  // we draw teleport image (if any) in last
361  if (teleport != nullptr) res.push_back(teleport);
362 
363  return res;
364 }
365 
366 struct flash_fade_step_renderer
367 {
368  std::vector<texture> images;
369  std::chrono::steady_clock::time_point start_time;
370 
371  void operator()(const rect& dest) const {
372  auto now = std::chrono::steady_clock::now();
373  auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
374 
376  // PHASE 1: White Flash (Additive)
377  float intensity = 1.0f - (static_cast<float>(elapsed.count()) /
379  uint8_t flash_val = static_cast<uint8_t>(intensity * 255);
380 
381  for(texture t : images) {
382  draw::blit(t, dest); // Base
383 
384  t.set_blend_mode(SDL_BLENDMODE_ADD);
385  t.set_alpha_mod(flash_val);
386  draw::blit(t, dest); // Additive pass
387  draw::blit(t, dest); // Additive pass
388 
389  t.set_blend_mode(SDL_BLENDMODE_BLEND);
390  t.set_alpha_mod(255);
391  }
392  }
394  // PHASE 2: Alpha Fade
395  auto fade_elapsed = elapsed - game_display::FLASH_FADE_ROUTE_FLASH_DURATION;
396  float progress = static_cast<float>(fade_elapsed.count()) /
398 
399  uint8_t alpha = static_cast<uint8_t>((1.0f - progress) * 255);
400 
401  for(texture t : images) {
402  t.set_alpha_mod(alpha);
403  draw::blit(t, dest);
404  t.set_alpha_mod(255);
405  }
406  }
407  }
408 };
409 
410 } //anonymous namespace
411 
413 {
414  const bool on_map = context().map().on_board(loc);
415  const bool is_shrouded = shrouded(loc);
416 
418 
419  if(cursor::get() == cursor::WAIT) {
420  // Interaction is disabled, so we don't need anything else
421  return;
422  }
423 
424  if(on_map && loc == mouseoverHex_ && !map_screenshot_) {
426  const unit* u = context().get_visible_unit(loc, viewing_team());
427  if(u != nullptr) {
428  hex_top_layer = drawing_layer::mouseover_top;
429  }
430 
431  const image::locator* mo_top_path;
432  const image::locator* mo_bot_path;
433 
434  if(u == nullptr) {
435  mo_top_path = &mouseover_normal_top;
436  mo_bot_path = &mouseover_normal_bot;
437  } else if(viewing_team().is_enemy(u->side())) {
438  mo_top_path = &mouseover_enemy_top;
439  mo_bot_path = &mouseover_enemy_bot;
440  } else if(viewing_team().side() == u->side()) {
441  mo_top_path = &mouseover_self_top;
442  mo_bot_path = &mouseover_self_bot;
443  } else {
444  mo_top_path = &mouseover_ally_top;
445  mo_bot_path = &mouseover_ally_bot;
446  }
447 
448  drawing_buffer_add(hex_top_layer, loc,
449  [tex = image::get_texture(*mo_top_path, image::HEXED)](const rect& dest) { draw::blit(tex, dest); });
450 
452  [tex = image::get_texture(*mo_bot_path, image::HEXED)](const rect& dest) { draw::blit(tex, dest); });
453  }
454 
455  // Draw reach_map information.
456  if(!is_shrouded && !reach_map_.empty() && reach_map_.find(loc) != reach_map_.end()) {
457  // draw the reachmap tint below units and high terrain graphics
458  std::string color = prefs::get().reach_map_color();
459  std::string tint_opacity = std::to_string(prefs::get().reach_map_tint_opacity());
460 
461  drawing_buffer_add(drawing_layer::reachmap_highlight, loc, [tex = image::get_texture(game_config::reach_map_prefix + ".png~RC(magenta>"+color+")~O("+tint_opacity+"%)", image::HEXED)](const rect& dest) {
462  draw::blit(tex, dest);
463  });
464  // We remove the reachmap border mask of the hovered hex to avoid weird interactions with other visual objects.
465  if(loc != mouseoverHex_) {
466  // draw the highlight borders on top of units and terrain
468  for(const texture& t : images) {
469  draw::blit(t, dest);
470  }
471  });
472  }
473  }
474 
475  // Check if Whiteboard is active to avoid overlapping path visualizations.
476  if(std::shared_ptr<wb::manager> w = wb_.lock()) {
477  w->draw_hex(loc);
478 
479  if(!(w->is_active() && w->has_temp_move())) {
480 
481  // Draw standard footsteps for the current turn's route.
482  std::vector<texture> footstepImages = footsteps_images(loc, route_, dc_);
483  if(!footstepImages.empty()) {
484  drawing_buffer_add(drawing_layer::footsteps, loc, [images = std::move(footstepImages)](const rect& dest) {
485  for(const texture& t : images) {
486  draw::blit(t, dest);
487  }
488  });
489  }
490 
491  // Draw the flash fade effect for queued (future turn) moves.
493  std::vector<texture> images = footsteps_images(loc, flash_fade_route_, dc_);
494  if(!images.empty()) {
496  flash_fade_step_renderer{ std::move(images), flash_fade_route_start_time_ });
497  }
498  }
499  }
500  }
501 
502  // Draw the attack direction indicator
503  if(on_map && loc == attack_indicator_src_) {
505  [tex = image::get_texture("misc/attack-indicator-src-" + attack_indicator_direction() + ".png", image::HEXED)](const rect& dest)
506  { draw::blit(tex, dest); }
507  );
508  } else if(on_map && loc == attack_indicator_dst_) {
510  [tex = image::get_texture("misc/attack-indicator-dst-" + attack_indicator_direction() + ".png", image::HEXED)](const rect& dest)
511  { draw::blit(tex, dest); }
512  );
513  }
514 
515  // Linger overlay unconditionally otherwise it might give glitches
516  // so it's drawn over the shroud and fog.
517  if(mode_ != RUNNING) {
520  [tex = image::get_texture(linger, image::TOD_COLORED)](const rect& dest) { draw::blit(tex, dest); });
521  }
522 
523  if(on_map && loc == selectedHex_ && !game_config::images::selected.empty()) {
526  [tex = image::get_texture(selected, image::HEXED)](const rect& dest) { draw::blit(tex, dest); });
527  }
528 
529  // Show def% and turn to reach info
530  if(!is_shrouded && on_map) {
532  }
533 }
534 
536 {
538 }
539 
541 {
543 }
544 
546 {
547  display::layout();
548 
549  // We need teams for the reports below
550  if(context().teams().empty()) {
551  return;
552  }
553 
554  refresh_report("report_clock");
555  refresh_report("report_battery");
556  refresh_report("report_countdown");
557 
559  {
560  wb::future_map future; // start planned unit map scope
561 
562  // We display the unit the mouse is over if it is over a unit,
563  // otherwise we display the unit that is selected.
564  for (const std::string &name : reports_object_->report_list()) {
565  refresh_report(name);
566  }
567  invalidateGameStatus_ = false;
568  }
569 }
570 
571 
573 {
574  if(mode != mode_) {
575  mode_ = mode;
576  invalidate_all();
577  }
578 }
579 
581 {
582  // Search if there is a mark here
584 
585  std::shared_ptr<wb::manager> wb = wb_.lock();
586 
587  // Don't use empty route or the first step (the unit will be there)
588  if(w != route_.marks.end()
589  && !route_.steps.empty() && route_.steps.front() != loc) {
590  const unit_map::const_iterator un =
591  (wb && wb->get_temp_move_unit().valid()) ?
592  wb->get_temp_move_unit() : context().units().find(route_.steps.front());
593  if(un != context().units().end()) {
594  // Display the def% of this terrain
595  int def = 100 - un->defense_modifier(context().map().get_terrain(loc), loc);
596  std::stringstream def_text;
597  def_text << def << "%";
598 
599  color_t color = game_config::red_to_green(def, false);
600 
601  // simple mark (no turn point) use smaller font
602  int def_font = w->second.turns > 0 ? 18 : 16;
603  draw_text_in_hex(loc, drawing_layer::move_info, def_text.str(), def_font, color);
604 
606  [inv = w->second.invisible, zoc = w->second.zoc, cap = w->second.capture](const rect& dest) {
607  if(inv) {
608  draw::blit(image::get_texture(image::locator{"misc/hidden.png"}, image::HEXED), dest);
609  }
610 
611  if(zoc) {
612  draw::blit(image::get_texture(image::locator{"misc/zoc.png"}, image::HEXED), dest);
613  }
614 
615  if(cap) {
616  draw::blit(image::get_texture(image::locator{"misc/capture.png"}, image::HEXED), dest);
617  }
618  });
619 
620  //we display turn info only if different from a simple last "1"
621  if (w->second.turns > 1 || (w->second.turns == 1 && loc != route_.steps.back())) {
622  std::stringstream turns_text;
623  turns_text << w->second.turns;
624  draw_text_in_hex(loc, drawing_layer::move_info, turns_text.str(), 17, font::NORMAL_COLOR, 0.5,0.8);
625  }
626 
627  // The hex is full now, so skip the "show enemy moves"
628  return;
629  }
630  }
631  // When out-of-turn, it's still interesting to check out the terrain defs of the selected unit
632  else if (selectedHex_.valid() && loc == mouseoverHex_)
633  {
634  const unit_map::const_iterator selectedUnit = resources::gameboard->find_visible_unit(selectedHex_,viewing_team());
635  const unit_map::const_iterator mouseoveredUnit = resources::gameboard->find_visible_unit(mouseoverHex_,viewing_team());
636  if(selectedUnit != context().units().end() && mouseoveredUnit == context().units().end()) {
637  // Display the def% of this terrain
638  int def = 100 - selectedUnit->defense_modifier(context().map().get_terrain(loc), loc);
639  std::stringstream def_text;
640  def_text << def << "%";
641 
642  color_t color = game_config::red_to_green(def, false);
643 
644  // use small font
645  int def_font = 16;
646  draw_text_in_hex(loc, drawing_layer::move_info, def_text.str(), def_font, color);
647  }
648  }
649 
650  if (!reach_map_.empty()) {
651  reach_map::iterator reach = reach_map_.find(loc);
652  if (reach != reach_map_.end() && reach->second > 1) {
653  const std::string num = std::to_string(reach->second);
654  draw_text_in_hex(loc, drawing_layer::move_info, num, 16, font::YELLOW_COLOR);
655  }
656  }
657 }
658 
660 {
662  highlight_another_reach(paths_list);
663 }
664 
666  const map_location& goal)
667 {
668  // Fold endpoints of routes into reachability map.
669  for (const pathfind::paths::step &dest : paths_list.destinations) {
670  reach_map_[dest.curr]++;
671  }
672  reach_map_changed_ = true;
673 
674  if(goal != map_location::null_location() && paths_list.destinations.contains(goal)) {
675  const auto& path_to_goal = paths_list.destinations.get_path(paths_list.destinations.find(goal));
676  const map_location enemy_unit_location = path_to_goal[0];
677  units_that_can_reach_goal_.insert(enemy_unit_location);
678  }
679 }
680 
682 {
684  if(!reach_map_.empty()) {
685  reach_map_.clear();
686  reach_map_changed_ = true;
687  return true;
688  } else {
689  return false;
690  }
691 }
692 
694 {
695  for(const map_location& step : route_.steps) {
696  invalidate(step);
697  }
698 }
699 
701 {
703 
704  if(route != nullptr) {
705  route_ = *route;
706  } else {
707  route_.steps.clear();
708  route_.marks.clear();
709  }
710 
712 }
713 
714 void game_display::float_label(const map_location& loc, const std::string& text, const color_t& color)
715 {
716  if(prefs::get().floating_labels() == false || fogged(loc)) {
717  return;
718  }
719 
720  rect loc_rect = get_location_rect(loc);
721 
722  using namespace std::chrono_literals;
723  const auto lifetime = 1s / turbo_speed();
724 
725  // Base speed is 100 pixels per second, taken in milliseconds
726  const double pixels_per_millisecond = 0.1 * turbo_speed() * get_zoom_factor();
727 
728  font::floating_label flabel(text);
730  flabel.set_color(color);
731  flabel.set_position(loc_rect.center().x, loc_rect.y); // middle of top edge
732  flabel.set_move(0, -pixels_per_millisecond); // moving up
733  flabel.set_lifetime(0ms, std::chrono::round<std::chrono::milliseconds>(lifetime));
735 
736  font::add_floating_label(flabel);
737 }
738 
740 {
744 
747 
750  }
751 }
752 
754 {
756 }
757 
759 {
760  in_game_ = true;
761  create_buttons();
762  invalidate_all();
763 }
764 
766  if (b) {
767  needs_rebuild_ = true;
768  }
769 }
770 
772  if (needs_rebuild_) {
773  needs_rebuild_ = false;
775  invalidate_all();
776  rebuild_all();
777  return true;
778  }
779  return false;
780 }
781 
783 {
784  return overlay_map_;
785 }
786 
787 std::vector<texture> game_display::get_reachmap_images(const map_location& loc) const
788 {
789  std::vector<std::string> names;
790  const auto adjacent = get_adjacent_tiles(loc);
791 
792  enum visibility { REACH = 0, ENEMY = 1, CLEAR = 2 };
793  std::array<visibility, 6> tiles;
794 
795  for(int i = 0; i < 6; ++i) {
796  // look for units adjacent to loc
797  std::string test_location = std::to_string(adjacent[i].x) + "," + std::to_string(adjacent[i].y);
798  const unit *u = context().get_visible_unit(adjacent[i], viewing_team());
799  if(reach_map_.find(adjacent[i]) != reach_map_.end()) {
800  DBG_DP << test_location << " is REACHABLE";
801  tiles[i] = REACH;
802  }
803  /**
804  * Make sure there is a unit selected or displayed when drawing the reachmap with enemy detection.
805  * Enemy detection needs to be "disabled" when the reach_map_team_index_ failsafes to viewing_team_index.
806  */
808  DBG_DP << test_location << " is NOT REACHABLE";
809  tiles[i] = CLEAR;
810  }
811  // Grab the reachmap-context team index updated in "display::process_reachmap_changes()" and test for adjacent enemy units
812  else if(u != nullptr && !u->incapacitated() && resources::gameboard->get_team(display::reach_map_team_index_).is_enemy(u->side())) {
813  DBG_DP << test_location << " has an attackable ENEMY";
814  tiles[i] = ENEMY;
815  } else {
816  DBG_DP << test_location << " is NOT REACHABLE";
817  tiles[i] = CLEAR;
818  }
819  }
820 
821  // Find out if location is in the inner part of reachmap (surrounded by reach)
822  int s;
823  for(s = 0; s != 6; ++s) {
824  if(tiles[s] != REACH) {
825  break;
826  }
827  }
828 
829  if(s == 6) {
830  // Completely surrounded by reach. This may have a special graphic.
831  DBG_DP << "Tried completely surrounding";
832  std::string name = game_config::reach_map_prefix + "-all.png";
833  if(image::exists(name)) {
834  names.push_back(std::move(name));
835  }
836  }
837 
838  // Find all the directions overlap occurs from
839  for(int i = 0, cap1 = 0; cap1 != 6; ++cap1) {
840  if(tiles[i] != REACH) {
841  std::ostringstream stream;
842  std::string suffix;
843  std::string name;
845 
846  std::string color = prefs::get().reach_map_color();
847  std::string enemy_color = prefs::get().reach_map_enemy_color();
848  std::string border_opacity = std::to_string(prefs::get().reach_map_border_opacity());
849 
850  if(tiles[i] == ENEMY) {
851  suffix = ".png~RC(magenta>"+enemy_color+")~O("+border_opacity+"%)";
852  } else {
853  suffix = ".png~RC(magenta>"+color+")~O("+border_opacity+"%)";
854  }
855 
856  for(int cap2 = 0; tiles[i] != REACH && cap2 != 6; i = (i + 1) % 6, ++cap2) {
858  if(!image::exists(stream.str() + ".png")) {
859  DBG_DP << "Image does not exist: " << stream.str() + ".png on " << loc;
860  // If we don't have any surface at all,
861  // then move onto the next overlapped area
862  if(name.empty()) {
863  i = (i + 1) % 6;
864  }
865  break;
866  } else {
867  name = stream.str();
868  }
869  }
870 
871  if(!name.empty()) {
872  names.push_back(name + suffix);
873  }
874  } else {
875  i = (i + 1) % 6;
876  }
877  }
878 
879  // now get the textures
880  std::vector<texture> res;
881 
882  for(const std::string& name : names) {
883  DBG_DP << "Pushing: " << name;
884  if(texture tex = image::get_texture(name, image::HEXED)) {
885  res.push_back(std::move(tex));
886  }
887  }
888 
889  return res;
890 }
static bool is_enemy(std::size_t side, std::size_t other_side)
Definition: abilities.cpp:1063
map_location loc
Definition: move.cpp:172
const std::vector< map_location > & route_
Definition: move.cpp:386
double t
Definition: astarsearch.cpp:63
std::vector< std::string > names
Definition: build_info.cpp:74
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
const unit * get_visible_unit(const map_location &loc, const team &current_team, bool see_all=false) const
virtual const gamemap & map() const =0
virtual const unit_map & units() const =0
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:88
const team & viewing_team() const
Definition: display.cpp:332
bool map_screenshot_
Used to indicate to drawing functions that we are doing a map screenshot.
Definition: display.hpp:861
void draw_text_in_hex(const map_location &loc, const drawing_layer layer, const std::string &text, std::size_t font_size, color_t color, double x_in_hex=0.5, double y_in_hex=0.5)
Draw text on a hex.
Definition: display.cpp:1352
std::size_t viewing_team_index_
Definition: display.hpp:712
std::map< map_location, std::vector< overlay > > overlay_map
Definition: display.hpp:883
map_location selectedHex_
Definition: display.hpp:760
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.cpp:1459
bool unit_can_draw_here(const map_location &loc, const unit &unit) const
Returns true if there is no exclusive draw request for loc, or if there is, that it's for unit.
Definition: display.cpp:376
virtual void render() override
Update offscreen render buffers.
Definition: display.cpp:2294
void fade_tod_mask(const std::string &old, const std::string &new_)
ToD mask smooth fade.
Definition: display.cpp:2089
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:2970
double turbo_speed() const
Definition: display.cpp:2008
bool reach_map_changed_
Definition: display.hpp:878
static double get_zoom_factor()
Returns the current zoom factor.
Definition: display.hpp:259
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:305
void rebuild_all()
Rebuild all dynamic terrain.
Definition: display.cpp:423
virtual void layout() override
Finalize screen layout.
Definition: display.cpp:2263
virtual void highlight_hex(map_location hex)
Definition: display.cpp:1383
void update_tod(const time_of_day *tod_override=nullptr)
Applies r,g,b coloring to the map.
Definition: display.cpp:382
void process_reachmap_changes()
Definition: display.cpp:3141
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:1858
map_location mouseoverHex_
Definition: display.hpp:761
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:664
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:2963
void drawing_buffer_add(const drawing_layer layer, const map_location &loc, decltype(draw_helper::do_draw) draw_func)
Add an item to the drawing buffer.
Definition: display.cpp:1235
std::size_t reach_map_team_index_
Definition: display.hpp:880
const display_context & context() const
Definition: display.hpp:184
rect get_location_rect(const map_location &loc) const
Returns the on-screen rect corresponding to a loc.
Definition: display.cpp:680
std::set< map_location > invalidated_
Definition: display.hpp:750
void create_buttons()
Definition: display.cpp:826
bool invalidateGameStatus_
Definition: display.hpp:738
virtual void draw_invalidated()
Only called when there's actual redrawing to do.
Definition: display.cpp:2450
reports * reports_object_
Definition: display.hpp:740
virtual void update() override
Update animations and internal state.
Definition: display.cpp:2246
virtual void draw_hex(const map_location &loc)
Redraws a single gamemap location.
Definition: display.cpp:2486
const std::unique_ptr< fake_unit_manager > fake_unit_man_
Definition: display.hpp:731
const display_context * dc_
Definition: display.hpp:669
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.cpp:659
bool dont_show_all_
Definition: display.hpp:713
void refresh_report(const std::string &report_name, const config *new_cfg=nullptr)
Update the given report.
Definition: display.cpp:2719
std::weak_ptr< wb::manager > wb_
Definition: display.hpp:671
virtual void select_hex(map_location hex)
Definition: display.cpp:1375
reach_map reach_map_
Definition: display.hpp:876
void set_move(double xmove, double ymove)
void set_position(double xpos, double ypos)
void set_lifetime(const std::chrono::milliseconds &lifetime, const std::chrono::milliseconds &fadeout=std::chrono::milliseconds{100})
void set_color(const color_t &color)
void set_scroll_mode(LABEL_SCROLL_MODE scroll)
void set_font_size(int font_size)
Game board class.
Definition: game_board.hpp:47
team & get_team(int i)
Definition: game_board.hpp:92
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:185
virtual void draw_invalidated() override
Only called when there's actual redrawing to do.
void draw_movement_info(const map_location &loc)
Draws the movement info (turns available) for a given location.
void display_unit_hex(map_location hex)
Change the unit to be displayed in the sidebar.
void invalidate_unit_after_move(const map_location &src, const map_location &dst)
Same as invalidate_unit() if moving the displayed unit.
std::string attack_indicator_direction() const
Function to get attack direction suffix.
game_mode mode_
void flash_fade_route(const pathfind::marked_route &route)
Visualizes a path by flashing it white then fade out over 2 seconds.
virtual void highlight_hex(map_location hex) override
Function to highlight a location.
map_location displayedUnitHex_
void invalidate_route()
std::vector< texture > get_reachmap_images(const map_location &loc) const
void scroll_to_leader(int side, SCROLL_TYPE scroll_type=ONSCREEN, bool force=true)
Scrolls to the leader of a certain side.
static constexpr std::chrono::milliseconds FLASH_FADE_ROUTE_FLASH_DURATION
void set_game_mode(const game_mode mode)
virtual void draw_hex(const map_location &loc) override
Redraws a single gamemap location.
void set_attack_indicator(const map_location &src, const map_location &dst)
Set the attack direction indicator.
const std::unique_ptr< display_chat_manager > chat_man_
pathfind::marked_route route_
virtual void render() override
TLD render() override.
void set_route(const pathfind::marked_route *route)
Sets the route along which footsteps are drawn to show movement of a unit.
virtual overlay_map & get_overlays() override
Inherited from display.
static constexpr std::chrono::milliseconds FLASH_FADE_ROUTE_TOTAL_DURATION
void update_flash_fade_route()
static constexpr std::chrono::milliseconds FLASH_FADE_ROUTE_FADE_DURATION
bool flash_fade_route_active_
std::set< map_location > units_that_can_reach_goal_
void invalidate_unit()
Function to invalidate that unit status displayed on the sidebar.
virtual void select_hex(map_location hex) override
Function to display a location as selected.
pathfind::marked_route flash_fade_route_
void highlight_reach(const pathfind::paths &paths_list)
Sets the paths that are currently displayed as available for the unit to move along.
void new_turn()
Update lighting settings.
void clear_attack_indicator()
virtual const time_of_day & get_time_of_day(const map_location &loc) const override
void highlight_another_reach(const pathfind::paths &paths_list, const map_location &goal=map_location::null_location())
Add more paths to highlight.
map_location attack_indicator_dst_
game_mode
Sets the linger mode for the display.
@ RUNNING
no linger overlay, show fog and shroud.
std::chrono::steady_clock::time_point flash_fade_route_start_time_
bool unhighlight_reach()
Reset highlighting of paths.
void needs_rebuild(bool b)
Sets whether the screen (map visuals) needs to be rebuilt.
overlay_map overlay_map_
bool maybe_rebuild()
Rebuilds the screen if needs_rebuild(true) was previously called, and resets the flag.
virtual void layout() override
TLD layout() override.
virtual bool has_time_area() const override
game_display(game_board &board, std::weak_ptr< wb::manager > wb, reports &reports_object, const std::string &theme_id, const config &level)
virtual void update() override
TLD update() override.
map_location attack_indicator_src_
void float_label(const map_location &loc, const std::string &text, const color_t &color)
Function to float a label above a tile.
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:265
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:347
Generic locator abstracting the location of an image.
Definition: picture.hpp:59
static prefs & get()
std::string reach_map_color()
std::string reach_map_enemy_color()
const std::set< std::string > & report_list()
Definition: reports.cpp:1917
static bool is_synced()
bool is_enemy(int n) const
Definition: team.hpp:267
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition: texture.hpp:33
bool has_time_area() const
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:57
const time_of_day & get_previous_time_of_day() const
void redraw_unit(const unit &u) const
draw a unit.
Definition: drawer.cpp:202
unit_iterator end()
Definition: map.hpp:428
unit_iterator find(std::size_t id)
Definition: map.cpp:302
unit_iterator find_leader(int side)
Definition: map.cpp:320
This class represents a single unit of a specific type.
Definition: unit.hpp:39
Drawing functions, for drawing things on the screen.
drawing_layer
@ linger_overlay
The overlay used for the linger mode.
@ reachmap_highlight
Overlay on reachable hexes.
@ footsteps
Footsteps showing path from unit to mouse.
@ attack_indicator
Layer which holds the attack indicator.
@ reachmap_border
Overlay border of reachable hexes.
@ mouseover_top
Top half of image following the mouse.
@ mouseover_bottom
Bottom half of image following the mouse.
@ move_info
Movement info (defense%, etc...)
@ selected_hex
Image on the selected unit.
std::size_t i
Definition: function.cpp:1031
static lg::log_domain log_engine("engine")
static lg::log_domain log_display("display")
#define DBG_DP
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:826
int side() const
The side this unit belongs to.
Definition: unit.hpp:249
T end(const std::pair< T, T > &p)
void get_adjacent_tiles(const map_location &a, utils::span< map_location, 6 > res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:513
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:541
Standard logging facilities (interface).
bool is_shrouded(const display *disp, const map_location &loc)
Our definition of map labels being obscured is if the tile is obscured, or the tile below is obscured...
Definition: label.cpp:33
CURSOR_TYPE get()
Definition: cursor.cpp:205
@ WAIT
Definition: cursor.hpp:28
void blit(const texture &tex, const ::rect &dst)
Draws a texture, or part of a texture, at the given location.
Definition: draw.cpp:409
const int SIZE_FLOAT_LABEL
Definition: constants.cpp:32
const color_t YELLOW_COLOR
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
const color_t NORMAL_COLOR
@ ANCHOR_LABEL_MAP
std::string selected
std::string foot_teleport_enter
std::string foot_teleport_exit
int reach_map_border_opacity
std::string reach_map_prefix
Definition: game_config.cpp:58
int reach_map_tint_opacity
color_t red_to_green(double val, bool for_text)
Return a color corresponding to the value val red for val=0.0 to green for val=100....
Functions to load and save images from/to disk.
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:855
@ HEXED
Standard hexagonal tile mask applied, removing portions that don't fit.
Definition: picture.hpp:175
@ TOD_COLORED
Same as HEXED, but with Time of Day color tint applied.
Definition: picture.hpp:177
texture get_texture(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image texture suitable for hardware-accelerated rendering.
Definition: picture.cpp:955
Unit and team statistics.
::tod_manager * tod_manager
Definition: resources.cpp:29
game_board * gameboard
Definition: resources.cpp:20
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:87
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:141
Definition: display.hpp:45
std::string to_string(const Range &range, const Func &op)
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
int w
Definition: pathfind.cpp:188
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:51
Encapsulates the map of the game.
Definition: location.hpp:46
static std::string write_direction(direction dir)
Definition: location.cpp:154
bool valid() const
Definition: location.hpp:111
direction
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:48
static const map_location & null_location()
Definition: location.hpp:103
Structure which holds a single route and marks for special events.
Definition: pathfind.hpp:142
std::vector< map_location > & steps
Definition: pathfind.hpp:187
std::vector< map_location > get_path(const const_iterator &) const
Returns the path going from the source point (included) to the destination point j (excluded).
Definition: pathfind.cpp:497
bool contains(const map_location &) const
Definition: pathfind.cpp:514
const_iterator find(const map_location &) const
Definition: pathfind.cpp:478
map_location curr
Definition: pathfind.hpp:89
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:73
dest_vect destinations
Definition: pathfind.hpp:101
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:49
constexpr point center() const
The center point of the rectangle, accounting for origin.
Definition: rect.hpp:106
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
std::string image_mask
The image that is to be laid over all images while this time of day lasts.
Definition: time_of_day.hpp:96
bool valid() const
Definition: map.hpp:273
Applies the planned unit map for the duration of the struct's life.
Definition: manager.hpp:253
static map_location::direction s
#define h
#define b