The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
minimap.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "global.hpp"
18 #include "minimap.hpp"
19 
20 #include "game_board.hpp"
21 #include "gettext.hpp"
22 #include "image.hpp"
23 #include "log.hpp"
24 #include "map/map.hpp"
25 #include "resources.hpp"
26 #include "sdl/utils.hpp"
27 #include "team.hpp"
28 #include "terrain/type_data.hpp"
29 #include "wml_exception.hpp"
30 #include "formula/string_utils.hpp"
31 
32 #include "game_display.hpp"
33 
34 #include "preferences.hpp"
35 
36 static lg::log_domain log_display("display");
37 #define DBG_DP LOG_STREAM(debug, log_display)
38 #define WRN_DP LOG_STREAM(warn, log_display)
39 
40 
41 namespace image {
42 
43 surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::map<map_location,unsigned int> *reach_map)
44 {
45  const terrain_type_data & tdata = *map.tdata();
46 
47  const int scale = 8;
48 
49  DBG_DP << "creating minimap " << int(map.w()*scale*0.75) << "," << map.h()*scale << "\n";
50 
51  const bool preferences_minimap_draw_terrain = preferences::minimap_draw_terrain();
52  const bool preferences_minimap_terrain_coding = preferences::minimap_terrain_coding();
53  const bool preferences_minimap_draw_villages = preferences::minimap_draw_villages();
54  const bool preferences_minimap_unit_coding = preferences::minimap_movement_coding();
55 
56  const size_t map_width = map.w()*scale*3/4;
57  const size_t map_height = map.h()*scale;
58  if(map_width == 0 || map_height == 0) {
59  return surface(nullptr);
60  }
61 
62  if(!preferences_minimap_draw_villages && !preferences_minimap_draw_terrain)
63  {
64  //return if there is nothing to draw.
65  //(optimisation)
66  double ratio = std::min<double>( w*1.0 / map_width, h*1.0 / map_height);
67  return create_neutral_surface(map_width * ratio, map_height * ratio);
68  }
69 
70  surface minimap(create_neutral_surface(map_width, map_height));
71  if(minimap == nullptr)
72  return surface(nullptr);
73 
74  typedef mini_terrain_cache_map cache_map;
75  cache_map *normal_cache = &mini_terrain_cache;
76  cache_map *fog_cache = &mini_fogged_terrain_cache;
77  cache_map *highlight_cache = &mini_highlighted_terrain_cache;
78 
79  for(int y = 0; y != map.total_height(); ++y)
80  for(int x = 0; x != map.total_width(); ++x) {
81 
82  const map_location loc(x,y);
83  if(!map.on_board(loc))
84  continue;
85 
86  const bool highlighted = reach_map && reach_map->count(loc) != 0;
87 
88  const bool shrouded = (resources::screen != nullptr && resources::screen->is_blindfolded()) || (vw != nullptr && vw->shrouded(loc));
89  // shrouded hex are not considered fogged (no need to fog a black image)
90  const bool fogged = (vw != nullptr && !shrouded && vw->fogged(loc));
91 
92  const t_translation::t_terrain terrain = shrouded ?
93  t_translation::VOID_TERRAIN : map[loc];
94  const terrain_type& terrain_info = tdata.get_terrain_info(terrain);
95 
96  // we need a balanced shift up and down of the hexes.
97  // if not, only the bottom half-hexes are clipped
98  // and it looks asymmetrical.
99 
100  // also do 1-pixel shift because the scaling
101  // function seems to do it with its rounding
102  SDL_Rect maprect = sdl::create_rect(
103  x * scale * 3 / 4 - 1
104  , y * scale + scale / 4 * (is_odd(x) ? 1 : -1) - 1
105  , 0
106  , 0);
107 
108  if (preferences_minimap_draw_terrain) {
109 
110  if (preferences_minimap_terrain_coding) {
111 
112  surface surf(nullptr);
113 
114  bool need_fogging = false;
115  bool need_highlighting = false;
116 
117  cache_map* cache = fogged ? fog_cache : normal_cache;
118  if (highlighted)
119  cache = highlight_cache;
120  cache_map::iterator i = cache->find(terrain);
121 
122  if (fogged && i == cache->end()) {
123  // we don't have the fogged version in cache
124  // try the normal cache and ask fogging the image
125  cache = normal_cache;
126  i = cache->find(terrain);
127  need_fogging = true;
128  }
129 
130  if (highlighted && i == cache->end()) {
131  // we don't have the highlighted version in cache
132  // try the normal cache and ask fogging the image
133  cache = normal_cache;
134  i = cache->find(terrain);
135  need_highlighting = true;
136  }
137 
138  if(i == cache->end() && !terrain_info.minimap_image().empty()) {
139  std::string base_file =
140  "terrain/" + terrain_info.minimap_image() + ".png";
141  surface tile = get_image(base_file,image::HEXED);
142 
143  //Compose images of base and overlay if necessary
144  // NOTE we also skip overlay when base is missing (to avoid hiding the error)
145  if(tile != nullptr && tdata.get_terrain_info(terrain).is_combined() && !terrain_info.minimap_image_overlay().empty()) {
146  std::string overlay_file =
147  "terrain/" + terrain_info.minimap_image_overlay() + ".png";
148  surface overlay = get_image(overlay_file,image::HEXED);
149 
150  if(overlay != nullptr && overlay != tile) {
151  surface combined = create_neutral_surface(tile->w, tile->h);
152  SDL_Rect r = sdl::create_rect(0,0,0,0);
153  sdl_blit(tile, nullptr, combined, &r);
154  r.x = std::max(0, (tile->w - overlay->w)/2);
155  r.y = std::max(0, (tile->h - overlay->h)/2);
156  //blit_surface needs neutral surface
157  surface overlay_neutral = make_neutral_surface(overlay);
158  blit_surface(overlay_neutral, nullptr, combined, &r);
159  tile = combined;
160  }
161  }
162 
163  surf = scale_surface_sharp(tile, scale, scale);
164 
165  i = normal_cache->insert(cache_map::value_type(terrain,surf)).first;
166  }
167 
168  surf = i->second;
169 
170  if (need_fogging) {
171  surf = adjust_surface_color(surf,-50,-50,-50);
172  fog_cache->insert(cache_map::value_type(terrain,surf));
173  }
174 
175  if (need_highlighting) {
176  surf = adjust_surface_color(surf,50,50,50);
177  highlight_cache->insert(cache_map::value_type(terrain,surf));
178  }
179 
180  if(surf != nullptr)
181  sdl_blit(surf, nullptr, minimap, &maprect);
182 
183  } else {
184 
185  SDL_Color 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 = create_color(0,0,0,0);
189  } else
190  col = int_to_color(it->second.rep());
191 
192  bool first = true;
193  const t_translation::t_list& underlying_terrains = tdata.underlying_union_terrain(terrain);
194  for(const t_translation::t_terrain& underlying_terrain : underlying_terrains) {
195 
196  const std::string& terrain_id = tdata.get_terrain_info(underlying_terrain).id();
197  std::map<std::string, color_range>::const_iterator it = game_config::team_rgb_range.find(terrain_id);
198  if (it == game_config::team_rgb_range.end())
199  continue;
200 
201  SDL_Color tmp = int_to_color(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 = sdl::create_rect(maprect.x, maprect.y, scale * 3/4, scale);
231  const Uint32 mapped_col = SDL_MapRGB(minimap->format,col.r,col.g,col.b);
232  sdl::fill_rect(minimap, &fillrect, mapped_col);
233  }
234  }
235 
236  if (terrain_info.is_village() && preferences_minimap_draw_villages) {
237 
238  int side = (resources::gameboard ? resources::gameboard->village_owner(loc) : -1); //check needed for mp create dialog
239 
240  SDL_Color col = int_to_color(game_config::team_rgb_range.find("white")->second.min());
241 
242  if (!fogged) {
243  if (side > -1) {
244 
245  if (preferences_minimap_unit_coding || !vw ) {
246  col = team::get_minimap_color(side + 1);
247  } else {
248 
249  if (vw->owns_village(loc))
251  else if (vw->is_enemy(side + 1))
253  else
255  }
256  }
257  }
258 
259  SDL_Rect fillrect = sdl::create_rect(
260  maprect.x
261  , maprect.y
262  , scale * 3/4
263  , scale
264  );
265 
266  const Uint32 mapped_col = SDL_MapRGB(minimap->format,col.r,col.g,col.b);
267  sdl::fill_rect(minimap, &fillrect, mapped_col);
268 
269  }
270 
271  }
272 
273  double wratio = w*1.0 / minimap->w;
274  double hratio = h*1.0 / minimap->h;
275  double ratio = std::min<double>(wratio, hratio);
276 
277  minimap = scale_surface_sharp(minimap,
278  static_cast<int>(minimap->w * ratio), static_cast<int>(minimap->h * ratio));
279 
280  DBG_DP << "done generating minimap\n";
281 
282  return minimap;
283 }
284 
285 #ifdef SDL_GPU
286 SDL_Rect draw_minimap(CVideo &video, const SDL_Rect &area, const gamemap &map, const team *vw, const std::map<map_location, unsigned int> *reach_map)
287 {
288  const terrain_type_data & tdata = *map.tdata();
289 
290  const float width = map.w() * 72 * 3/4;
291  const float height = map.h() * 72 + 72 * 1/4;
292  const float scale_factor = std::min(area.w / width, area.h / height);
293  const float tile_size = 72 * scale_factor;
294  const int xoff = area.x + (area.w - scale_factor * width) / 2;
295  const int yoff = area.y + (area.h - scale_factor * height) / 2 + 1;
296 
297  const bool draw_terrain = preferences::minimap_draw_terrain();
298  const bool terrain_coding = preferences::minimap_terrain_coding();
299  const bool draw_villages = preferences::minimap_draw_villages();
300 
301  for(int y = 0; y != map.total_height(); ++y) {
302  for(int x = 0; x != map.total_width(); ++x) {
303  map_location loc(x, y);
304  if(!map.on_board(loc))
305  continue;
306 
307  const bool highlighted = reach_map && reach_map->count(loc) != 0;
308  const bool shrouded = (resources::screen != nullptr && resources::screen->is_blindfolded()) || (vw != nullptr && vw->shrouded(loc));
309  // shrouded hex are not considered fogged (no need to fog a black image)
310  const bool fogged = (vw != nullptr && !shrouded && vw->fogged(loc));
311 
312  const t_translation::t_terrain terrain = shrouded ?
313  t_translation::VOID_TERRAIN : map[loc];
314  const terrain_type& terrain_info = tdata.get_terrain_info(terrain);
315 
316  const int xpos = x * tile_size * 3/4 + xoff;
317  const int ypos = y * tile_size + tile_size / 4 * (is_odd(x) ? 2 : 0) + yoff;
318 
319  if (draw_terrain) {
320  if (terrain_coding) {
321  sdl::timage img = image::get_texture("terrain/" + terrain_info.minimap_image() + ".png", image::HEXED);
322  //TODO: proper color mod values once blending is implemented
323  if (fogged) {
324  img.set_color_mod(100, 100, 100);
325  }
326  if (highlighted) {
327  img.set_color_mod(150, 150, 150);
328  }
329 
330  img.set_scale(scale_factor, scale_factor);
331 
332  video.draw_texture(img, xpos, ypos);
333 
334  if (terrain_info.is_combined()) {
335  sdl::timage overlay = image::get_texture("terrain/" + terrain_info.minimap_image_overlay() + ".png", image::HEXED);
336  if (fogged) {
337  overlay.set_color_mod(100, 100, 100);
338  }
339  if (highlighted) {
340  overlay.set_color_mod(150, 150, 150);
341  }
342 
343  overlay.set_scale(scale_factor, scale_factor);
344  video.draw_texture(overlay, xpos, ypos);
345  }
346  } else {
347  SDL_Color col;
348  std::map<std::string, color_range>::const_iterator it = game_config::team_rgb_range.find(terrain_info.id());
349  if (it == game_config::team_rgb_range.end()) {
350  col = create_color(0,0,0,SDL_ALPHA_OPAQUE);
351  } else
352  col = int_to_color(it->second.rep());
353 
354  bool first = true;
355  const t_translation::t_list& underlying_terrains = tdata.underlying_union_terrain(terrain);
356  for(const t_translation::t_terrain& underlying_terrain : underlying_terrains) {
357 
358  const std::string& terrain_id = tdata.get_terrain_info(underlying_terrain).id();
359  std::map<std::string, color_range>::const_iterator it = game_config::team_rgb_range.find(terrain_id);
360  if (it == game_config::team_rgb_range.end())
361  continue;
362 
363  SDL_Color tmp = int_to_color(it->second.rep());
364 
365  if (fogged) {
366  if (tmp.b < 50) tmp.b = 0;
367  else tmp.b -= 50;
368  if (tmp.g < 50) tmp.g = 0;
369  else tmp.g -= 50;
370  if (tmp.r < 50) tmp.r = 0;
371  else tmp.r -= 50;
372  }
373 
374  if (highlighted) {
375  if (tmp.b > 205) tmp.b = 255;
376  else tmp.b += 50;
377  if (tmp.g > 205) tmp.g = 255;
378  else tmp.g += 50;
379  if (tmp.r > 205) tmp.r = 255;
380  else tmp.r += 50;
381  }
382 
383  if (first) {
384  first = false;
385  col = tmp;
386  } else {
387  col.r = col.r - (col.r - tmp.r)/2;
388  col.g = col.g - (col.g - tmp.g)/2;
389  col.b = col.b - (col.b - tmp.b)/2;
390  }
391  }
392  SDL_Rect fillrect = sdl::create_rect(xpos, ypos, tile_size * 3/4, tile_size);
393  sdl::fill_rect(video, fillrect, col);
394  }
395  }
396 
397 
398  if (terrain_info.is_village() && draw_villages) {
399 
400  int side = (resources::gameboard ? resources::gameboard->village_owner(loc) : -1); //check needed for mp create dialog
401 
402  SDL_Color col = int_to_color(game_config::team_rgb_range.find("white")->second.min());
403 
404  if (!fogged) {
405  if (side > -1) {
406 
407  if (!preferences::minimap_movement_coding() || !vw ) {
408  col = team::get_minimap_color(side + 1);
409  } else {
410 
411  if (vw->owns_village(loc))
413  else if (vw->is_enemy(side + 1))
415  else
417  }
418  }
419  }
420 
421  SDL_Rect fillrect = sdl::create_rect(
422  xpos
423  , ypos
424  , tile_size * 3/4
425  , tile_size
426  );
427 
428  sdl::fill_rect(video, fillrect, col);
429  }
430  }
431  }
432 
433  return sdl::create_rect(xoff, yoff, width * scale_factor, height * scale_factor);
434 }
435 #endif
436 
437 }
int total_width() const
Real width of the map, including borders.
Definition: map.hpp:114
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:878
const t_translation::t_list & underlying_union_terrain(const t_translation::t_terrain &terrain) const
Definition: type_data.cpp:93
std::string unmoved_color()
surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::map< map_location, unsigned int > *reach_map)
function to create the minimap for a given map the surface returned must be freed by the user ...
Definition: minimap.cpp:43
surface create_neutral_surface(int w, int h)
Definition: utils.cpp:150
std::map< t_translation::t_terrain, surface > mini_terrain_cache_map
Definition: image.hpp:140
void fill_rect(surface &dst, SDL_Rect *dst_rect, const Uint32 color)
Fill a rectangle on a given surface.
Definition: rect.hpp:143
GLenum GLenum GLenum GLenum GLenum scale
Definition: glew.h:10669
std::map< std::string, color_range > team_rgb_range
SDL_Color create_color(const unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
Definition: utils.cpp:89
bool shrouded(const map_location &loc) const
Definition: team.cpp:567
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
Uint32 rep() const
High-contrast shade, intended for the minimap markers.
Definition: color_range.hpp:88
const std::string & minimap_image_overlay() const
Definition: terrain.hpp:31
bool owns_village(const map_location &loc) const
Definition: team.hpp:190
SDL_Color int_to_color(const Uint32 rgb)
Definition: utils.cpp:63
Definition: video.hpp:58
bool minimap_movement_coding()
game_display * screen
Definition: resources.cpp:27
mini_terrain_cache_map mini_fogged_terrain_cache
Definition: image.cpp:186
bool is_enemy(int n) const
Definition: team.hpp:247
bool is_odd(T num)
Definition: util.hpp:37
const std::string & id() const
Definition: terrain.hpp:37
int village_owner(const map_location &loc) const
Given the location of a village, will return the 0-based index of the team that currently owns it...
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
bool is_combined() const
Definition: terrain.hpp:77
-file sdl_utils.hpp
#define DBG_DP
Definition: minimap.cpp:37
const tdata_cache & tdata() const
Definition: map.hpp:70
int total_height() const
Real height of the map, including borders.
Definition: map.hpp:117
void blit_surface(const surface &surf, const SDL_Rect *srcrect, surface &dst, const SDL_Rect *dstrect)
Replacement for sdl_blit.
Definition: utils.cpp:2185
bool minimap_draw_villages()
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:50
GLuint GLuint end
Definition: glew.h:1221
static lg::log_domain log_display("display")
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
int w() const
Effective map width.
Definition: map.hpp:105
game_board * gameboard
Definition: resources.cpp:20
const terrain_type & get_terrain_info(const t_translation::t_terrain &terrain) const
Get the corresponding terrain_type information object for a given type of terrain.
Definition: type_data.cpp:53
Encapsulates the map of the game.
Definition: map.hpp:37
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:135
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
bool is_village() const
Definition: terrain.hpp:62
bool is_blindfolded() const
Definition: display.cpp:517
surf
Definition: filter.cpp:143
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:47
Encapsulates the map of the game.
Definition: location.hpp:38
surface adjust_surface_color(const surface &surf, int red, int green, int blue, bool optimize)
Definition: utils.cpp:718
std::string allied_color()
int h() const
Effective map height.
Definition: map.hpp:108
static SDL_Color get_minimap_color(int side)
Definition: team.cpp:833
static tcache cache
Definition: minimap.cpp:139
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
size_t i
Definition: function.cpp:1057
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
mini_terrain_cache_map mini_highlighted_terrain_cache
Definition: image.cpp:187
bool fogged(const map_location &loc) const
Definition: team.cpp:575
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
mini_terrain_cache_map mini_terrain_cache
Definition: image.cpp:185
GLint GLint GLint GLint GLint GLint GLsizei GLsizei height
Definition: glew.h:1220
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an empty SDL_Rect.
Definition: rect.cpp:28
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:467
const t_terrain VOID_TERRAIN
bool minimap_draw_terrain()
bool find(E event, F functor)
Tests whether an event handler is available.
GLint GLvoid * img
Definition: glew.h:1353
surface make_neutral_surface(const surface &surf)
Definition: utils.cpp:135
const color_range & color_info(const std::string &name)
this module manages the cache of images.
Definition: image.cpp:75
Standard logging facilities (interface).
GLint * first
Definition: glew.h:1496
GLint GLint GLint GLint GLint GLint GLsizei width
Definition: glew.h:1220
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:112
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
surface scale_surface_sharp(const surface &surf, int w, int h, bool optimize)
Scale a surface using modified nearest neighbour algorithm.
Definition: utils.cpp:577
const std::string & minimap_image() const
Definition: terrain.hpp:30
GLsizei const GLcharARB ** string
Definition: glew.h:4503
std::string enemy_color()
std::vector< t_terrain > t_list
Definition: translation.hpp:75
bool minimap_terrain_coding()