The Battle for Wesnoth  1.19.26+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  // Fall back to the unit's race, which a [unit] may have overridden
313  if(footprints.empty()) {
314  footprints = u->race()->footprints();
315  }
316  }
317 
318  if(footprints.empty()) {
319  footprints = "default";
320  }
321  const std::string foot_speed_prefix = "footsteps/" + footprints + "/";
322 
323  // Generate a red tint string based on movement cost.
324  std::string color_mod;
325  if(move_cost > 1) {
326  int reduction = std::min(255, (move_cost - 1) * 85);
327  color_mod = "~CS(0,-" + std::to_string(reduction) + ",-" + std::to_string(reduction) + ")";
328  }
329 
330  texture teleport;
331 
332  // We draw 2 half-hex (with possibly different directions),
333  // but skip the first for the first step.
334  const int first_half = (i == route.steps.begin()) ? 1 : 0;
335  // and the second for the last step
336  const int second_half = (i+1 == route.steps.end()) ? 0 : 1;
337 
338  for (int h = first_half; h <= second_half; ++h) {
339  const std::string sense( h==0 ? "-in" : "-out" );
340 
341  if (!tiles_adjacent(*(i+(h-1)), *(i+h))) {
342  std::string teleport_image =
344  teleport = image::get_texture(teleport_image, image::HEXED);
345  continue;
346  }
347 
348  // In function of the half, use the incoming or outgoing direction
349  map_location::direction dir = (i+(h-1))->get_relative_dir(*(i+h));
350 
351  std::string rotate;
353  // No image, take the opposite direction and do a 180 rotation
354  dir = i->get_opposite_direction(dir);
355  rotate = "~FL(horiz)~FL(vert)";
356  }
357 
358  const std::string image = foot_speed_prefix + "footprint"
359  + sense + "-" + i->write_direction(dir)
360  + ".png" + rotate + color_mod;
361 
362  res.push_back(image::get_texture(image, image::HEXED));
363  }
364 
365  // we draw teleport image (if any) in last
366  if (teleport != nullptr) res.push_back(teleport);
367 
368  return res;
369 }
370 
371 struct flash_fade_step_renderer
372 {
373  std::vector<texture> images;
374  std::chrono::steady_clock::time_point start_time;
375 
376  void operator()(const rect& dest) const {
377  auto now = std::chrono::steady_clock::now();
378  auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
379 
381  // PHASE 1: White Flash (Additive)
382  float intensity = 1.0f - (static_cast<float>(elapsed.count()) /
384  uint8_t flash_val = static_cast<uint8_t>(intensity * 255);
385 
386  for(texture t : images) {
387  draw::blit(t, dest); // Base
388 
389  t.set_blend_mode(SDL_BLENDMODE_ADD);
390  t.set_alpha_mod(flash_val);
391  draw::blit(t, dest); // Additive pass
392  draw::blit(t, dest); // Additive pass
393 
394  t.set_blend_mode(SDL_BLENDMODE_BLEND);
395  t.set_alpha_mod(255);
396  }
397  }
399  // PHASE 2: Alpha Fade
400  auto fade_elapsed = elapsed - game_display::FLASH_FADE_ROUTE_FLASH_DURATION;
401  float progress = static_cast<float>(fade_elapsed.count()) /
403 
404  uint8_t alpha = static_cast<uint8_t>((1.0f - progress) * 255);
405 
406  for(texture t : images) {
407  t.set_alpha_mod(alpha);
408  draw::blit(t, dest);
409  t.set_alpha_mod(255);
410  }
411  }
412  }
413 };
414 
415 } //anonymous namespace
416 
418 {
419  const bool on_map = context().map().on_board(loc);
420  const bool is_shrouded = shrouded(loc);
421 
423 
424  if(cursor::get() == cursor::WAIT) {
425  // Interaction is disabled, so we don't need anything else
426  return;
427  }
428 
429  if(on_map && loc == mouseoverHex_ && !map_screenshot_) {
431  const unit* u = context().get_visible_unit(loc, viewing_team());
432  if(u != nullptr) {
433  hex_top_layer = drawing_layer::mouseover_top;
434  }
435 
436  const image::locator* mo_top_path;
437  const image::locator* mo_bot_path;
438 
439  if(u == nullptr) {
440  mo_top_path = &mouseover_normal_top;
441  mo_bot_path = &mouseover_normal_bot;
442  } else if(viewing_team().is_enemy(u->side())) {
443  mo_top_path = &mouseover_enemy_top;
444  mo_bot_path = &mouseover_enemy_bot;
445  } else if(viewing_team().side() == u->side()) {
446  mo_top_path = &mouseover_self_top;
447  mo_bot_path = &mouseover_self_bot;
448  } else {
449  mo_top_path = &mouseover_ally_top;
450  mo_bot_path = &mouseover_ally_bot;
451  }
452 
453  drawing_buffer_add(hex_top_layer, loc,
454  [tex = image::get_texture(*mo_top_path, image::HEXED)](const rect& dest) { draw::blit(tex, dest); });
455 
457  [tex = image::get_texture(*mo_bot_path, image::HEXED)](const rect& dest) { draw::blit(tex, dest); });
458  }
459 
460  // Draw reach_map information.
461  if(!is_shrouded && !reach_map_.empty() && reach_map_.find(loc) != reach_map_.end()) {
462  // draw the reachmap tint below units and high terrain graphics
463  std::string color = prefs::get().reach_map_color();
464  std::string tint_opacity = std::to_string(prefs::get().reach_map_tint_opacity());
465 
466  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) {
467  draw::blit(tex, dest);
468  });
469  // We remove the reachmap border mask of the hovered hex to avoid weird interactions with other visual objects.
470  if(loc != mouseoverHex_) {
471  // draw the highlight borders on top of units and terrain
473  for(const texture& t : images) {
474  draw::blit(t, dest);
475  }
476  });
477  }
478  }
479 
480  // Check if Whiteboard is active to avoid overlapping path visualizations.
481  if(std::shared_ptr<wb::manager> w = wb_.lock()) {
482  w->draw_hex(loc);
483 
484  if(!(w->is_active() && w->has_temp_move())) {
485 
486  // Draw standard footsteps for the current turn's route.
487  std::vector<texture> footstepImages = footsteps_images(loc, route_, dc_);
488  if(!footstepImages.empty()) {
489  drawing_buffer_add(drawing_layer::footsteps, loc, [images = std::move(footstepImages)](const rect& dest) {
490  for(const texture& t : images) {
491  draw::blit(t, dest);
492  }
493  });
494  }
495 
496  // Draw the flash fade effect for queued (future turn) moves.
498  std::vector<texture> images = footsteps_images(loc, flash_fade_route_, dc_);
499  if(!images.empty()) {
501  flash_fade_step_renderer{ std::move(images), flash_fade_route_start_time_ });
502  }
503  }
504  }
505  }
506 
507  // Draw the attack direction indicator
508  if(on_map && loc == attack_indicator_src_) {
510  [tex = image::get_texture("misc/attack-indicator-src-" + attack_indicator_direction() + ".png", image::HEXED)](const rect& dest)
511  { draw::blit(tex, dest); }
512  );
513  } else if(on_map && loc == attack_indicator_dst_) {
515  [tex = image::get_texture("misc/attack-indicator-dst-" + attack_indicator_direction() + ".png", image::HEXED)](const rect& dest)
516  { draw::blit(tex, dest); }
517  );
518  }
519 
520  // Linger overlay unconditionally otherwise it might give glitches
521  // so it's drawn over the shroud and fog.
522  if(mode_ != RUNNING) {
525  [tex = image::get_texture(linger, image::TOD_COLORED)](const rect& dest) { draw::blit(tex, dest); });
526  }
527 
528  if(on_map && loc == selectedHex_ && !game_config::images::selected.empty()) {
531  [tex = image::get_texture(selected, image::HEXED)](const rect& dest) { draw::blit(tex, dest); });
532  }
533 
534  // Show def% and turn to reach info
535  if(!is_shrouded && on_map) {
537  }
538 }
539 
541 {
543 }
544 
546 {
548 }
549 
551 {
552  display::layout();
553 
554  // We need teams for the reports below
555  if(context().teams().empty()) {
556  return;
557  }
558 
559  refresh_report("report_clock");
560  refresh_report("report_battery");
561  refresh_report("report_countdown");
562 
564  {
565  wb::future_map future; // start planned unit map scope
566 
567  // We display the unit the mouse is over if it is over a unit,
568  // otherwise we display the unit that is selected.
569  for (const std::string &name : reports_object_->report_list()) {
570  refresh_report(name);
571  }
572  invalidateGameStatus_ = false;
573  }
574 }
575 
576 
578 {
579  if(mode != mode_) {
580  mode_ = mode;
581  invalidate_all();
582  }
583 }
584 
586 {
587  // Search if there is a mark here
589 
590  std::shared_ptr<wb::manager> wb = wb_.lock();
591 
592  // Don't use empty route or the first step (the unit will be there)
593  if(w != route_.marks.end()
594  && !route_.steps.empty() && route_.steps.front() != loc) {
595  const unit_map::const_iterator un =
596  (wb && wb->get_temp_move_unit().valid()) ?
597  wb->get_temp_move_unit() : context().units().find(route_.steps.front());
598  if(un != context().units().end()) {
599  // Display the def% of this terrain
600  int def = 100 - un->defense_modifier(context().map().get_terrain(loc), loc);
601  std::stringstream def_text;
602  def_text << def << "%";
603 
604  color_t color = game_config::red_to_green(def, false);
605 
606  // simple mark (no turn point) use smaller font
607  int def_font = w->second.turns > 0 ? 18 : 16;
608  draw_text_in_hex(loc, drawing_layer::move_info, def_text.str(), def_font, color);
609 
611  [inv = w->second.invisible, zoc = w->second.zoc, cap = w->second.capture](const rect& dest) {
612  if(inv) {
613  draw::blit(image::get_texture(image::locator{"misc/hidden.png"}, image::HEXED), dest);
614  }
615 
616  if(zoc) {
617  draw::blit(image::get_texture(image::locator{"misc/zoc.png"}, image::HEXED), dest);
618  }
619 
620  if(cap) {
621  draw::blit(image::get_texture(image::locator{"misc/capture.png"}, image::HEXED), dest);
622  }
623  });
624 
625  //we display turn info only if different from a simple last "1"
626  if (w->second.turns > 1 || (w->second.turns == 1 && loc != route_.steps.back())) {
627  std::stringstream turns_text;
628  turns_text << w->second.turns;
629  draw_text_in_hex(loc, drawing_layer::move_info, turns_text.str(), 17, font::NORMAL_COLOR, 0.5,0.8);
630  }
631 
632  // The hex is full now, so skip the "show enemy moves"
633  return;
634  }
635  }
636  // When out-of-turn, it's still interesting to check out the terrain defs of the selected unit
637  else if (selectedHex_.valid() && loc == mouseoverHex_)
638  {
639  const unit_map::const_iterator selectedUnit = resources::gameboard->find_visible_unit(selectedHex_,viewing_team());
640  const unit_map::const_iterator mouseoveredUnit = resources::gameboard->find_visible_unit(mouseoverHex_,viewing_team());
641  if(selectedUnit != context().units().end() && mouseoveredUnit == context().units().end()) {
642  // Display the def% of this terrain
643  int def = 100 - selectedUnit->defense_modifier(context().map().get_terrain(loc), loc);
644  std::stringstream def_text;
645  def_text << def << "%";
646 
647  color_t color = game_config::red_to_green(def, false);
648 
649  // use small font
650  int def_font = 16;
651  draw_text_in_hex(loc, drawing_layer::move_info, def_text.str(), def_font, color);
652  }
653  }
654 
655  if (!reach_map_.empty()) {
656  reach_map::iterator reach = reach_map_.find(loc);
657  if (reach != reach_map_.end() && reach->second > 1) {
658  const std::string num = std::to_string(reach->second);
659  draw_text_in_hex(loc, drawing_layer::move_info, num, 16, font::YELLOW_COLOR);
660  }
661  }
662 }
663 
665 {
667  highlight_another_reach(paths_list);
668 }
669 
671  const map_location& goal)
672 {
673  // Fold endpoints of routes into reachability map.
674  for (const pathfind::paths::step &dest : paths_list.destinations) {
675  reach_map_[dest.curr]++;
676  }
677  reach_map_changed_ = true;
678 
679  if(goal != map_location::null_location() && paths_list.destinations.contains(goal)) {
680  const auto& path_to_goal = paths_list.destinations.get_path(paths_list.destinations.find(goal));
681  const map_location enemy_unit_location = path_to_goal[0];
682  units_that_can_reach_goal_.insert(enemy_unit_location);
683  }
684 }
685 
687 {
689  if(!reach_map_.empty()) {
690  reach_map_.clear();
691  reach_map_changed_ = true;
692  return true;
693  } else {
694  return false;
695  }
696 }
697 
699 {
700  for(const map_location& step : route_.steps) {
701  invalidate(step);
702  }
703 }
704 
706 {
708 
709  if(route != nullptr) {
710  route_ = *route;
711  } else {
712  route_.steps.clear();
713  route_.marks.clear();
714  }
715 
717 }
718 
719 void game_display::float_label(const map_location& loc, const std::string& text, const color_t& color)
720 {
721  if(prefs::get().floating_labels() == false || fogged(loc)) {
722  return;
723  }
724 
725  rect loc_rect = get_location_rect(loc);
726 
727  using namespace std::chrono_literals;
728  const auto lifetime = 1s / turbo_speed();
729 
730  // Base speed is 100 pixels per second, taken in milliseconds
731  const double pixels_per_millisecond = 0.1 * turbo_speed() * get_zoom_factor();
732 
733  font::floating_label flabel(text);
735  flabel.set_color(color);
736  flabel.set_position(loc_rect.center().x, loc_rect.y); // middle of top edge
737  flabel.set_move(0, -pixels_per_millisecond); // moving up
738  flabel.set_lifetime(0ms, std::chrono::round<std::chrono::milliseconds>(lifetime));
740 
741  font::add_floating_label(flabel);
742 }
743 
745 {
749 
752 
755  }
756 }
757 
759 {
761 }
762 
764 {
765  in_game_ = true;
766  create_buttons();
767  invalidate_all();
768 }
769 
771  if (b) {
772  needs_rebuild_ = true;
773  }
774 }
775 
777  if (needs_rebuild_) {
778  needs_rebuild_ = false;
780  invalidate_all();
781  rebuild_all();
782  return true;
783  }
784  return false;
785 }
786 
788 {
789  return overlay_map_;
790 }
791 
792 std::vector<texture> game_display::get_reachmap_images(const map_location& loc) const
793 {
794  std::vector<std::string> names;
795  const auto adjacent = get_adjacent_tiles(loc);
796 
797  enum visibility { REACH = 0, ENEMY = 1, CLEAR = 2 };
798  std::array<visibility, 6> tiles;
799 
800  for(int i = 0; i < 6; ++i) {
801  // look for units adjacent to loc
802  std::string test_location = std::to_string(adjacent[i].x) + "," + std::to_string(adjacent[i].y);
803  const unit *u = context().get_visible_unit(adjacent[i], viewing_team());
804  if(reach_map_.find(adjacent[i]) != reach_map_.end()) {
805  DBG_DP << test_location << " is REACHABLE";
806  tiles[i] = REACH;
807  }
808  /**
809  * Make sure there is a unit selected or displayed when drawing the reachmap with enemy detection.
810  * Enemy detection needs to be "disabled" when the reach_map_team_index_ failsafes to viewing_team_index.
811  */
813  DBG_DP << test_location << " is NOT REACHABLE";
814  tiles[i] = CLEAR;
815  }
816  // Grab the reachmap-context team index updated in "display::process_reachmap_changes()" and test for adjacent enemy units
817  else if(u != nullptr && !u->incapacitated() && resources::gameboard->get_team(display::reach_map_team_index_).is_enemy(u->side())) {
818  DBG_DP << test_location << " has an attackable ENEMY";
819  tiles[i] = ENEMY;
820  } else {
821  DBG_DP << test_location << " is NOT REACHABLE";
822  tiles[i] = CLEAR;
823  }
824  }
825 
826  // Find out if location is in the inner part of reachmap (surrounded by reach)
827  int s;
828  for(s = 0; s != 6; ++s) {
829  if(tiles[s] != REACH) {
830  break;
831  }
832  }
833 
834  if(s == 6) {
835  // Completely surrounded by reach. This may have a special graphic.
836  DBG_DP << "Tried completely surrounding";
837  std::string name = game_config::reach_map_prefix + "-all.png";
838  if(image::exists(name)) {
839  names.push_back(std::move(name));
840  }
841  }
842 
843  // Find all the directions overlap occurs from
844  for(int i = 0, cap1 = 0; cap1 != 6; ++cap1) {
845  if(tiles[i] != REACH) {
846  std::ostringstream stream;
847  std::string suffix;
848  std::string name;
850 
851  std::string color = prefs::get().reach_map_color();
852  std::string enemy_color = prefs::get().reach_map_enemy_color();
853  std::string border_opacity = std::to_string(prefs::get().reach_map_border_opacity());
854 
855  if(tiles[i] == ENEMY) {
856  suffix = ".png~RC(magenta>"+enemy_color+")~O("+border_opacity+"%)";
857  } else {
858  suffix = ".png~RC(magenta>"+color+")~O("+border_opacity+"%)";
859  }
860 
861  for(int cap2 = 0; tiles[i] != REACH && cap2 != 6; i = (i + 1) % 6, ++cap2) {
863  if(!image::exists(stream.str() + ".png")) {
864  DBG_DP << "Image does not exist: " << stream.str() + ".png on " << loc;
865  // If we don't have any surface at all,
866  // then move onto the next overlapped area
867  if(name.empty()) {
868  i = (i + 1) % 6;
869  }
870  break;
871  } else {
872  name = stream.str();
873  }
874  }
875 
876  if(!name.empty()) {
877  names.push_back(name + suffix);
878  }
879  } else {
880  i = (i + 1) % 6;
881  }
882  }
883 
884  // now get the textures
885  std::vector<texture> res;
886 
887  for(const std::string& name : names) {
888  DBG_DP << "Pushing: " << name;
889  if(texture tex = image::get_texture(name, image::HEXED)) {
890  res.push_back(std::move(tex));
891  }
892  }
893 
894  return res;
895 }
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