The Battle for Wesnoth  1.17.0-dev
minimap.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
18 #include "minimap.hpp"
19 
20 #include "color.hpp"
21 #include "display.hpp"
22 #include "game_board.hpp"
23 #include "gettext.hpp"
24 #include "picture.hpp"
25 #include "log.hpp"
26 #include "map/map.hpp"
27 #include "preferences/general.hpp"
28 #include "resources.hpp"
29 #include "sdl/surface.hpp"
30 #include "team.hpp"
31 #include "terrain/type_data.hpp"
32 
33 static lg::log_domain log_display("display");
34 #define DBG_DP LOG_STREAM(debug, log_display)
35 #define WRN_DP LOG_STREAM(warn, log_display)
36 
37 
38 namespace image {
39 
40 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)
41 {
42  const terrain_type_data & tdata = *map.tdata();
43 
44 
45  const bool preferences_minimap_draw_terrain = preferences::minimap_draw_terrain() || ignore_terrain_disabled;
46  const bool preferences_minimap_terrain_coding = preferences::minimap_terrain_coding();
47  const bool preferences_minimap_draw_villages = preferences::minimap_draw_villages();
48  const bool preferences_minimap_unit_coding = preferences::minimap_movement_coding();
49 
50  const int scale = (preferences_minimap_draw_terrain && preferences_minimap_terrain_coding) ? 24 : 4;
51 
52  DBG_DP << "creating minimap " << int(map.w()*scale*0.75) << "," << map.h()*scale << "\n";
53 
54  const std::size_t map_width = map.w()*scale*3/4;
55  const std::size_t map_height = map.h()*scale;
56  if(map_width == 0 || map_height == 0) {
57  return surface(nullptr);
58  }
59 
60  if(!preferences_minimap_draw_villages && !preferences_minimap_draw_terrain)
61  {
62  //return if there is nothing to draw.
63  //(optimisation)
64  double ratio = std::min<double>( w*1.0 / map_width, h*1.0 / map_height);
65  return surface(map_width * ratio, map_height * ratio);
66  }
67 
68  surface minimap(map_width, map_height);
69  if(minimap == nullptr)
70  return surface(nullptr);
71 
72  typedef mini_terrain_cache_map cache_map;
73  cache_map *normal_cache = &mini_terrain_cache;
74  cache_map *fog_cache = &mini_fogged_terrain_cache;
75  cache_map *highlight_cache = &mini_highlighted_terrain_cache;
76 
77  for(int y = 0; y <= map.total_height(); ++y)
78  for(int x = 0; x <= map.total_width(); ++x) {
79 
80  const map_location loc(x,y);
81  if(!map.on_board_with_border(loc))
82  continue;
83 
84  const bool shrouded = (display::get_singleton() != nullptr && display::get_singleton()->is_blindfolded()) || (vw != nullptr && vw->shrouded(loc));
85  // shrouded hex are not considered fogged (no need to fog a black image)
86  const bool fogged = (vw != nullptr && !shrouded && vw->fogged(loc));
87 
88  const bool highlighted = reach_map && reach_map->count(loc) != 0 && !shrouded;
89 
90  const t_translation::terrain_code terrain = shrouded ?
91  t_translation::VOID_TERRAIN : map[loc];
92  const terrain_type& terrain_info = tdata.get_terrain_info(terrain);
93 
94  // we need a balanced shift up and down of the hexes.
95  // if not, only the bottom half-hexes are clipped
96  // and it looks asymmetrical.
97 
98  SDL_Rect maprect {
99  x * scale * 3 / 4 - (scale / 4)
100  , y * scale + scale / 4 * (is_odd(x) ? 1 : -1) - (scale / 4)
101  , 0
102  , 0
103  };
104 
105  if (preferences_minimap_draw_terrain) {
106 
107  if (preferences_minimap_terrain_coding) {
108 
109  surface surf(nullptr);
110 
111  bool need_fogging = false;
112  bool need_highlighting = false;
113 
114  cache_map* cache = fogged ? fog_cache : normal_cache;
115  if (highlighted)
116  cache = highlight_cache;
117  cache_map::iterator i = cache->find(terrain);
118 
119  if (fogged && i == cache->end()) {
120  // we don't have the fogged version in cache
121  // try the normal cache and ask fogging the image
122  cache = normal_cache;
123  i = cache->find(terrain);
124  need_fogging = true;
125  }
126 
127  if (highlighted && i == cache->end()) {
128  // we don't have the highlighted version in cache
129  // try the normal cache and ask fogging the image
130  cache = normal_cache;
131  i = cache->find(terrain);
132  need_highlighting = true;
133  }
134 
135  if(i == cache->end() && !terrain_info.minimap_image().empty()) {
136  std::string base_file =
137  "terrain/" + terrain_info.minimap_image() + ".png";
138  surface tile = get_image(base_file,image::HEXED);
139 
140  //Compose images of base and overlay if necessary
141  // NOTE we also skip overlay when base is missing (to avoid hiding the error)
142  if(tile != nullptr && tdata.get_terrain_info(terrain).is_combined() && !terrain_info.minimap_image_overlay().empty()) {
143  std::string overlay_file =
144  "terrain/" + terrain_info.minimap_image_overlay() + ".png";
145  surface overlay = get_image(overlay_file,image::HEXED);
146 
147  if(overlay != nullptr && overlay != tile) {
148  surface combined(tile->w, tile->h);
149  SDL_Rect r {0,0,0,0};
150  sdl_blit(tile, nullptr, combined, &r);
151  r.x = std::max(0, (tile->w - overlay->w)/2);
152  r.y = std::max(0, (tile->h - overlay->h)/2);
153  sdl_blit(overlay, nullptr, combined, &r);
154  tile = combined;
155  }
156  }
157 
158  surf = scale_surface_sharp(tile, scale, scale);
159 
160  i = normal_cache->emplace(terrain, surf).first;
161  }
162 
163  if (i != cache->end())
164  {
165  surf = i->second;
166 
167  if (need_fogging) {
168  surf = adjust_surface_color(surf, -50, -50, -50);
169  fog_cache->emplace(terrain, surf);
170  }
171 
172  if (need_highlighting) {
173  surf = adjust_surface_color(surf, 50, 50, 50);
174  highlight_cache->emplace(terrain, surf);
175  }
176  }
177 
178  if(surf != nullptr)
179  sdl_blit(surf, nullptr, minimap, &maprect);
180 
181  } else {
182 
183  // Despite its name, game_config::team_rgb_range isn't just team colors,
184  // it has "red", "lightblue", "cave", "reef", "fungus", etc.
185  color_t col;
186  std::map<std::string, color_range>::const_iterator it = game_config::team_rgb_range.find(terrain_info.id());
187  if (it == game_config::team_rgb_range.end()) {
188  col = color_t(0,0,0,0);
189  } else
190  col = it->second.rep();
191 
192  bool first = true;
193  const t_translation::ter_list& underlying_terrains = tdata.underlying_union_terrain(terrain);
194  for(const t_translation::terrain_code& underlying_terrain : underlying_terrains) {
195 
196  const std::string& terrain_id = tdata.get_terrain_info(underlying_terrain).id();
197  it = game_config::team_rgb_range.find(terrain_id);
198  if (it == game_config::team_rgb_range.end())
199  continue;
200 
201  color_t tmp = it->second.rep();
202 
203  if (fogged) {
204  if (tmp.b < 50) tmp.b = 0;
205  else tmp.b -= 50;
206  if (tmp.g < 50) tmp.g = 0;
207  else tmp.g -= 50;
208  if (tmp.r < 50) tmp.r = 0;
209  else tmp.r -= 50;
210  }
211 
212  if (highlighted) {
213  if (tmp.b > 205) tmp.b = 255;
214  else tmp.b += 50;
215  if (tmp.g > 205) tmp.g = 255;
216  else tmp.g += 50;
217  if (tmp.r > 205) tmp.r = 255;
218  else tmp.r += 50;
219  }
220 
221  if (first) {
222  first = false;
223  col = tmp;
224  } else {
225  col.r = col.r - (col.r - tmp.r)/2;
226  col.g = col.g - (col.g - tmp.g)/2;
227  col.b = col.b - (col.b - tmp.b)/2;
228  }
229  }
230  SDL_Rect fillrect {maprect.x, maprect.y, scale * 3/4, scale};
231  const uint32_t mapped_col = SDL_MapRGB(minimap->format,col.r,col.g,col.b);
232  sdl::fill_surface_rect(minimap, &fillrect, mapped_col);
233  }
234  }
235 
236  if (terrain_info.is_village() && preferences_minimap_draw_villages) {
237 
238  int side_num = (resources::gameboard ? resources::gameboard->village_owner(loc) : 0); //check needed for mp create dialog
239 
240  // TODO: Add a key to [game_config][colors] for this
241  auto iter = game_config::team_rgb_range.find("white");
242  color_t col(255,255,255);
243  if(iter != game_config::team_rgb_range.end()) {
244  col = iter->second.min();
245  }
246 
247  if (!fogged) {
248  if (side_num > 0) {
249 
250  if (preferences_minimap_unit_coding || !vw ) {
251  col = team::get_minimap_color(side_num);
252  } else {
253 
254  if (vw->owns_village(loc))
256  else if (vw->is_enemy(side_num))
258  else
260  }
261  }
262  }
263 
264  SDL_Rect fillrect {
265  maprect.x
266  , maprect.y
267  , scale * 3/4
268  , scale
269  };
270 
271  const uint32_t mapped_col = SDL_MapRGB(minimap->format,col.r,col.g,col.b);
272  sdl::fill_surface_rect(minimap, &fillrect, mapped_col);
273 
274  }
275 
276  }
277 
278  double wratio = w*1.0 / minimap->w;
279  double hratio = h*1.0 / minimap->h;
280  double ratio = std::min<double>(wratio, hratio);
281 
282  minimap = scale_surface_sharp(minimap,
283  static_cast<int>(minimap->w * ratio), static_cast<int>(minimap->h * ratio));
284 
285  DBG_DP << "done generating minimap\n";
286 
287  return minimap;
288 }
289 
290 
291 }
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number of the team that currently owns it...
surface get_image(const image::locator &i_locator, TYPE type)
Caches and returns an image.
Definition: picture.cpp:816
std::string unmoved_color()
Definition: general.cpp:353
bool is_odd(T num)
Definition: math.hpp:36
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:92
std::map< std::string, color_range > team_rgb_range
Colors defined by WML [color_range] tags.
surface adjust_surface_color(const surface &surf, int red, int green, int blue)
Definition: utils.cpp:596
bool minimap_movement_coding()
Definition: general.cpp:805
mini_terrain_cache_map mini_fogged_terrain_cache
Definition: picture.cpp:217
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
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Get the corresponding terrain_type information object for a given type of terrain.
Definition: type_data.cpp:102
#define h
std::map< t_translation::terrain_code, surface > mini_terrain_cache_map
Definition: picture.hpp:158
surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::map< map_location, unsigned int > *reach_map, bool ignore_terrain_disabled)
function to create the minimap for a given map the surface returned must be freed by the user ...
Definition: minimap.cpp:40
Standard hexagonal tile mask applied, removing portions that don&#39;t fit.
Definition: picture.hpp:236
#define DBG_DP
Definition: minimap.cpp:34
const terrain_code VOID_TERRAIN
VOID_TERRAIN is used for shrouded hexes.
bool is_village() const
Definition: terrain.hpp:141
Contains the database of all known terrain types, both those defined explicitly by WML [terrain_type]...
Definition: type_data.hpp:40
bool minimap_draw_villages()
Definition: general.cpp:835
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
bool is_combined() const
True for instances created by the terrain_code(base, overlay) constructor.
Definition: terrain.hpp:167
void scale(size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, const ScalerCfg &cfg=ScalerCfg(), int yFirst=0, int yLast=std::numeric_limits< int >::max())
Definition: xbrz.cpp:1190
static lg::log_domain log_display("display")
int w() const
Effective map width.
Definition: map.hpp:50
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:390
game_board * gameboard
Definition: resources.cpp:21
Encapsulates the map of the game.
Definition: map.hpp:171
uint8_t r
Red value.
Definition: color.hpp:178
bool is_enemy(int n) const
Definition: team.hpp:255
map_display and display: classes which take care of displaying the map and game-data on the screen...
const std::shared_ptr< terrain_type_data > & tdata() const
Definition: map.hpp:204
Encapsulates the map of the game.
Definition: location.hpp:38
int total_width() const
Real width of the map, including borders.
Definition: map.hpp:59
bool shrouded(const map_location &loc) const
Definition: team.cpp:652
std::string allied_color()
Definition: general.cpp:313
std::size_t i
Definition: function.cpp:967
bool is_blindfolded() const
Definition: display.cpp:539
static tcache cache
Definition: minimap.cpp:124
mini_terrain_cache_map mini_highlighted_terrain_cache
Definition: picture.cpp:218
const std::string & minimap_image() const
Definition: terrain.hpp:45
int w
const t_translation::ter_list & underlying_union_terrain(const t_translation::terrain_code &terrain) const
Unordered set of all terrains used in either underlying_mvt_terrain or underlying_def_terrain.
Definition: type_data.cpp:143
int total_height() const
Real height of the map, including borders.
Definition: map.hpp:62
mini_terrain_cache_map mini_terrain_cache
Definition: picture.cpp:216
static color_t get_minimap_color(int side)
Definition: team.cpp:966
color_t rep() const
High-contrast shade, intended for the minimap markers.
Definition: color_range.hpp:95
bool minimap_draw_terrain()
Definition: general.cpp:845
const color_range & color_info(const std::string &name)
Functions to load and save images from/to disk.
surface scale_surface_sharp(const surface &surf, int w, int h)
Scale a surface using modified nearest neighbour algorithm.
Definition: utils.cpp:463
Standard logging facilities (interface).
const std::string & id() const
Definition: terrain.hpp:52
std::vector< terrain_code > ter_list
Definition: translation.hpp:78
bool fogged(const map_location &loc) const
Definition: team.cpp:661
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:32
bool owns_village(const map_location &loc) const
Definition: team.hpp:197
uint8_t g
Green value.
Definition: color.hpp:181
uint8_t b
Blue value.
Definition: color.hpp:184
int h() const
Effective map height.
Definition: map.hpp:53
const std::string & minimap_image_overlay() const
Definition: terrain.hpp:46
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::string enemy_color()
Definition: general.cpp:333
bool minimap_terrain_coding()
Definition: general.cpp:815