The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
halo.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 /**
16  * @file
17  * Maintain halo-effects for units and items.
18  * Examples: white mage, lighthouse.
19  */
20 
21 #include "global.hpp"
22 #include "animated.hpp"
23 #include "display.hpp"
24 #include "game_preferences.hpp"
25 #include "halo.hpp"
27 
28 #include <iostream>
29 
30 namespace halo
31 {
32 
33 class halo_impl
34 {
35 
36 class effect
37 {
38 public:
40  const map_location& loc, ORIENTATION, bool infinite);
41 
42  void set_location(int x, int y);
43 
44  bool render();
45  void unrender();
46 
47  bool expired() const { return !images_.cycles() && images_.animation_finished(); }
48  bool need_update() const { return images_.need_update(); }
49  bool does_change() const { return !images_.does_not_change(); }
50  bool on_location(const std::set<map_location>& locations) const;
51  bool location_not_known() const;
52 
53  void add_overlay_location(std::set<map_location>& locations);
54 private:
55 
57 
59 
61 
62  int x_, y_;
64  SDL_Rect rect_;
65 
66  /** The location of the center of the halo. */
68 
69  /** All locations over which the halo lies. */
70  std::vector<map_location> overlayed_hexes_;
71 
73 };
74 
76 
77 std::map<int, effect> haloes;
78 int halo_id;
79 
80 /**
81  * Upon unrendering, an invalidation list is send. All haloes in that area and
82  * the other invalidated haloes are stored in this set. Then there'll be
83  * tested which haloes overlap and they're also stored in this set.
84  */
85 std::set<int> invalidated_haloes;
86 
87 /**
88  * A newly added halo will be added to this list, these haloes don't need to be
89  * unrendered but do not to be rendered regardless which tiles are invalidated.
90  * These haloes will stay in this set until there're really rendered (rendering
91  * won't happen if for example the halo is offscreen).
92  */
93 std::set<int> new_haloes;
94 
95 /**
96  * Upon deleting, a halo isn't deleted but added to this set, upon unrendering
97  * the image is unrendered and deleted.
98  */
99 std::set<int> deleted_haloes;
100 
101 /**
102  * Haloes that have an animation or expiration time need to be checked every
103  * frame and are stored in this set.
104  */
105 std::set<int> changing_haloes;
106 
107 public:
108 /**
109  * impl's of exposed functions
110  */
111 
113  disp(&screen),
114  haloes(),
115  halo_id(1),
116  invalidated_haloes(),
117  new_haloes(),
118  deleted_haloes(),
119  changing_haloes()
120 {}
121 
122 
123 int add(int x, int y, const std::string& image, const map_location& loc,
124  ORIENTATION orientation=NORMAL, bool infinite=true);
125 
126 /** Set the position of an existing haloing effect, according to its handle. */
127 void set_location(int handle, int x, int y);
128 
129 /** Remove the halo with the given handle. */
130 void remove(int handle);
131 
132 /**
133  * Render and unrender haloes.
134  *
135  * Which haloes are rendered is determined by invalidated_locations and the
136  * internal state in the control sets (in halo.cpp).
137  */
138 void unrender(std::set<map_location> invalidated_locations);
139 void render();
140 
141 }; //end halo_impl
142 
144  const map_location& loc, ORIENTATION orientation, bool infinite) :
145  images_(img),
146  orientation_(orientation),
147  x_(0),
148  y_(0),
149  surf_(nullptr),
150  buffer_(nullptr),
151  rect_(sdl::empty_rect),
152  loc_(loc),
153  overlayed_hexes_(),
154  disp(screen)
155 {
156  assert(disp != nullptr);
157 
158  set_location(xpos,ypos);
159 
160  images_.start_animation(0,infinite);
161 
162 }
163 
165 {
166  int new_x = x - disp->get_location_x(map_location::ZERO());
167  int new_y = y - disp->get_location_y(map_location::ZERO());
168  if (new_x != x_ || new_y != y_) {
169  unrender();
170  x_ = new_x;
171  y_ = new_y;
172  buffer_.assign(nullptr);
173  overlayed_hexes_.clear();
174  }
175 }
176 
178 {
179  if(disp == nullptr) {
180  return false;
181  }
182 
183  if(loc_.x != -1 && loc_.y != -1) {
184  if(disp->shrouded(loc_)) {
185  return false;
186  } else {
187  // The location of a halo is an x,y value and not a map location.
188  // This means when a map is zoomed, the halo's won't move,
189  // This glitch is most visible on [item] haloes.
190  // This workaround always recalculates the location of the halo
191  // (item haloes have a location parameter to hide them under the shroud)
192  // and reapplies that location.
193  // It might be optimized by storing and comparing the zoom value.
194  set_location(
195  disp->get_location_x(loc_) + disp->hex_size() / 2,
196  disp->get_location_y(loc_) + disp->hex_size() / 2);
197  }
198  }
199 
200  images_.update_last_draw_time();
201  surf_.assign(image::get_image(current_image(),image::SCALED_TO_ZOOM));
202  if(surf_ == nullptr) {
203  return false;
204  }
205  if(orientation_ == HREVERSE || orientation_ == HVREVERSE) {
206  surf_.assign(image::reverse_image(surf_));
207  }
208  if(orientation_ == VREVERSE || orientation_ == HVREVERSE) {
209  surf_.assign(flop_surface(surf_));
210  }
211 
212  const int screenx = disp->get_location_x(map_location::ZERO());
213  const int screeny = disp->get_location_y(map_location::ZERO());
214 
215  const int xpos = x_ + screenx - surf_->w/2;
216  const int ypos = y_ + screeny - surf_->h/2;
217 
218  SDL_Rect rect = sdl::create_rect(xpos, ypos, surf_->w, surf_->h);
219  rect_ = rect;
220  SDL_Rect clip_rect = disp->map_outside_area();
221 
222  // If rendered the first time, need to determine the area affected.
223  // If a halo changes size, it is not updated.
224  if(location_not_known()) {
226  display::rect_of_hexes::iterator i = hexes.begin(), end = hexes.end();
227  for (;i != end; ++i) {
228  overlayed_hexes_.push_back(*i);
229  }
230  }
231 
232  if(sdl::rects_overlap(rect,clip_rect) == false) {
233  buffer_.assign(nullptr);
234  return false;
235  }
236 
238 
239  const clip_rect_setter clip_setter(screen, &clip_rect);
240  if(buffer_ == nullptr || buffer_->w != rect.w || buffer_->h != rect.h) {
241  SDL_Rect rect = rect_;
242  buffer_.assign(get_surface_portion(screen,rect));
243  } else {
244  SDL_Rect rect = rect_;
245  sdl_copy_portion(screen,&rect,buffer_,nullptr);
246  }
247 
248  sdl_blit(surf_,nullptr,screen,&rect);
249 
250  update_rect(rect_);
251 
252  return true;
253 }
254 
256 {
257  if (!surf_ || !buffer_) {
258  return;
259  }
260 
261  // Shrouded haloes are never rendered unless shroud has been re-placed; in
262  // that case, unrendering causes the hidden terrain (and previous halo
263  // frame, when dealing with animated halos) to glitch through shroud. We
264  // don't need to unrender them because shroud paints over the underlying
265  // area anyway.
266  if (loc_.x != -1 && loc_.y != -1 && disp->shrouded(loc_)) {
267  return;
268  }
269 
271 
272  SDL_Rect clip_rect = disp->map_outside_area();
273  const clip_rect_setter clip_setter(screen, &clip_rect);
274 
275  // Due to scrolling, the location of the rendered halo
276  // might have changed; recalculate
277  const int screenx = disp->get_location_x(map_location::ZERO());
278  const int screeny = disp->get_location_y(map_location::ZERO());
279 
280  const int xpos = x_ + screenx - surf_->w/2;
281  const int ypos = y_ + screeny - surf_->h/2;
282 
283  SDL_Rect rect = sdl::create_rect(xpos, ypos, surf_->w, surf_->h);
284  sdl_blit(buffer_,nullptr,screen,&rect);
285  update_rect(rect);
286 }
287 
288 bool halo_impl::effect::on_location(const std::set<map_location>& locations) const
289 {
290  for(std::vector<map_location>::const_iterator itor = overlayed_hexes_.begin();
291  itor != overlayed_hexes_.end(); ++itor) {
292  if(locations.find(*itor) != locations.end()) {
293  return true;
294  }
295  }
296  return false;
297 }
298 
300 {
301  return overlayed_hexes_.empty();
302 }
303 
305 {
306  for(std::vector<map_location>::const_iterator itor = overlayed_hexes_.begin();
307  itor != overlayed_hexes_.end(); ++itor) {
308 
309  locations.insert(*itor);
310  }
311 }
312 
313 // End halo_impl::effect impl's
314 
315 int halo_impl::add(int x, int y, const std::string& image, const map_location& loc,
316  ORIENTATION orientation, bool infinite)
317 {
318  const int id = halo_id++;
320  std::vector<std::string> items = utils::square_parenthetical_split(image, ',');
321  std::vector<std::string>::const_iterator itor = items.begin();
322  for(; itor != items.end(); ++itor) {
323  const std::vector<std::string>& items = utils::split(*itor, ':');
324  std::string str;
325  int time;
326 
327  if(items.size() > 1) {
328  str = items.front();
329  time = std::stoi(items.back());
330  } else {
331  str = *itor;
332  time = 100;
333  }
334  image_vector.push_back(animated<image::locator>::frame_description(time,image::locator(str)));
335 
336  }
337  haloes.insert(std::pair<int,effect>(id,effect(disp,x,y,image_vector,loc,orientation,infinite)));
338  new_haloes.insert(id);
339  if(haloes.find(id)->second.does_change() || !infinite) {
340  changing_haloes.insert(id);
341  }
342  return id;
343 }
344 
345 void halo_impl::set_location(int handle, int x, int y)
346 {
347  const std::map<int,effect>::iterator itor = haloes.find(handle);
348  if(itor != haloes.end()) {
349  itor->second.set_location(x,y);
350  }
351 }
352 
354 {
355  // Silently ignore invalid haloes.
356  // This happens when Wesnoth is being terminated as well.
357  if(handle == NO_HALO || haloes.find(handle) == haloes.end()) {
358  return;
359  }
360 
361  deleted_haloes.insert(handle);
362 }
363 
364 void halo_impl::unrender(std::set<map_location> invalidated_locations)
365 {
366  if(preferences::show_haloes() == false || haloes.empty()) {
367  return;
368  }
369  //assert(invalidated_haloes.empty());
370 
371  // Remove expired haloes
373  for(; itor != haloes.end(); ++itor ) {
374  if(itor->second.expired()) {
375  deleted_haloes.insert(itor->first);
376  }
377  }
378 
379  // Add the haloes marked for deletion to the invalidation set
380  std::set<int>::const_iterator set_itor = deleted_haloes.begin();
381  for(;set_itor != deleted_haloes.end(); ++set_itor) {
382  invalidated_haloes.insert(*set_itor);
383  haloes.find(*set_itor)->second.add_overlay_location(invalidated_locations);
384  }
385 
386  // Test the multi-frame haloes whether they need an update
387  for(set_itor = changing_haloes.begin();
388  set_itor != changing_haloes.end(); ++set_itor) {
389  if(haloes.find(*set_itor)->second.need_update()) {
390  invalidated_haloes.insert(*set_itor);
391  haloes.find(*set_itor)->second.add_overlay_location(invalidated_locations);
392  }
393  }
394 
395  // Find all halo's in a the invalidated area
396  size_t halo_count;
397 
398  // Repeat until set of haloes in the invalidated area didn't change
399  // (including none found) or all existing haloes are found.
400  do {
401  halo_count = invalidated_haloes.size();
402  for(itor = haloes.begin(); itor != haloes.end(); ++itor) {
403  // Test all haloes not yet in the set
404  // which match one of the locations
405  if(invalidated_haloes.find(itor->first) == invalidated_haloes.end() &&
406  (itor->second.location_not_known() ||
407  itor->second.on_location(invalidated_locations))) {
408 
409  // If found, add all locations which the halo invalidates,
410  // and add it to the set
411  itor->second.add_overlay_location(invalidated_locations);
412  invalidated_haloes.insert(itor->first);
413  }
414  }
415  } while (halo_count != invalidated_haloes.size() && halo_count != haloes.size());
416 
417  if(halo_count == 0) {
418  return;
419  }
420 
421  // Render the haloes:
422  // iterate through all the haloes and invalidate if in set
423  for(std::map<int, effect>::reverse_iterator ritor = haloes.rbegin(); ritor != haloes.rend(); ++ritor) {
424  if(invalidated_haloes.find(ritor->first) != invalidated_haloes.end()) {
425  ritor->second.unrender();
426  }
427  }
428 
429  // Really delete the haloes marked for deletion
430  for(set_itor = deleted_haloes.begin(); set_itor != deleted_haloes.end(); ++set_itor) {
431  // It can happen a deleted halo hasn't been rendered yet, invalidate them as well
432  new_haloes.erase(*set_itor);
433 
434  changing_haloes.erase(*set_itor);
435  invalidated_haloes.erase(*set_itor);
436  haloes.erase(*set_itor);
437  }
438 
439  deleted_haloes.clear();
440 }
441 
443 {
444  if(preferences::show_haloes() == false || haloes.empty() ||
445  (new_haloes.empty() && invalidated_haloes.empty())) {
446  return;
447  }
448 
449  // Keep track of not rendered new images they have to be kept scheduled
450  // for rendering otherwise the invalidation area is never properly set
451  std::set<int> unrendered_new_haloes;
452 
453  // Render the haloes:
454  // iterate through all the haloes and draw if in either set
456  itor != haloes.end(); ++itor) {
457 
458  if(new_haloes.find(itor->first) != new_haloes.end() &&
459  ! itor->second.render()) {
460 
461  unrendered_new_haloes.insert(itor->first);
462  } else if(invalidated_haloes.find(itor->first) != invalidated_haloes.end()) {
463  itor->second.render();
464  }
465  }
466 
467  invalidated_haloes.clear();
468  new_haloes = unrendered_new_haloes;
469 }
470 
471 // end halo_impl implementations
472 
473 // begin halo::manager
474 
476 {}
477 
478 handle manager::add(int x, int y, const std::string& image, const map_location& loc,
479  ORIENTATION orientation, bool infinite)
480 {
481  int new_halo = impl_->add(x,y,image, loc, orientation, infinite);
482  return handle(new halo_record(new_halo, impl_));
483 }
484 
485 /** Set the position of an existing haloing effect, according to its handle. */
486 void manager::set_location(const handle & h, int x, int y)
487 {
488  impl_->set_location(h->id_,x,y);
489 }
490 
491 /** Remove the halo with the given handle. */
492 void manager::remove(const handle & h)
493 {
494  impl_->remove(h->id_);
495  h->id_ = NO_HALO;
496 }
497 
498 /**
499  * Render and unrender haloes.
500  *
501  * Which haloes are rendered is determined by invalidated_locations and the
502  * internal state in the control sets (in halo.cpp).
503  */
504 void manager::unrender(std::set<map_location> invalidated_locations)
505 {
506  impl_->unrender(invalidated_locations);
507 }
508 
510 {
511  impl_->render();
512 }
513 
514 // end halo::manager implementation
515 
516 
517 /**
518  * halo::halo_record implementation
519  */
520 
522  id_(NO_HALO), //halo::NO_HALO
523  my_manager_()
524 {}
525 
527  id_(id),
528  my_manager_(my_manager)
529 {}
530 
532 {
533  if (!valid()) return;
534 
536 
537  if (man) {
538  try {
539  man->remove(id_);
540  } catch (std::exception & e) {
541  std::cerr << "Caught an exception in halo::halo_record destructor: \n" << e.what() << std::endl;
542  } catch (...) {}
543  }
544 }
545 
546 } //end namespace halo
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:878
manager(display &disp)
Definition: halo.cpp:475
tformula< unsigned > x_
The x coordinate of the rectangle.
Definition: canvas.cpp:682
std::vector< map_location > overlayed_hexes_
All locations over which the halo lies.
Definition: halo.cpp:70
iterator end() const
Definition: display.cpp:661
std::string id_
Definition: formula.cpp:636
animated< image::locator > images_
Definition: halo.cpp:58
bool rects_overlap(const SDL_Rect &rect1, const SDL_Rect &rect2)
Tests whether two rectangles overlap.
Definition: rect.cpp:52
bool cycles() const
Definition: animated.hpp:75
surface reverse_image(const surface &surf)
function to reverse an image.
Definition: image.cpp:1165
game_display * screen
Definition: resources.cpp:27
const SDL_Rect empty_rect
Definition: rect.cpp:26
halo_impl(display &screen)
impl's of exposed functions
Definition: halo.cpp:112
static const map_location & ZERO()
Old implementation:
Definition: location.hpp:190
map_location loc_
The location of the center of the halo.
Definition: halo.cpp:67
const int NO_HALO
Definition: halo.hpp:40
std::set< int > deleted_haloes
Upon deleting, a halo isn't deleted but added to this set, upon unrendering the image is unrendered a...
Definition: halo.cpp:99
std::set< int > new_haloes
A newly added halo will be added to this list, these haloes don't need to be unrendered but do not to...
Definition: halo.cpp:93
int get_location_x(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition: display.cpp:713
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
const std::vector< std::string > items
std::map< int, effect > haloes
Definition: halo.cpp:77
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:2335
Rectangular area of hexes, allowing to decide how the top and bottom edges handles the vertical shift...
Definition: display.hpp:309
void unrender(std::set< map_location > invalidated_locations)
Render and unrender haloes.
Definition: halo.cpp:364
int get_location_y(const map_location &loc) const
Definition: display.cpp:718
very simple iterator to walk into the rect_of_hexes
Definition: display.hpp:316
halo_record()
halo::halo_record implementation
Definition: halo.cpp:521
void add_overlay_location(std::set< map_location > &locations)
Definition: halo.cpp:304
map_location loc_
GLuint GLuint end
Definition: glew.h:1221
boost::weak_ptr< halo_impl > my_manager_
Definition: halo.hpp:95
GLuint id
Definition: glew.h:1647
bool on_location(const std::set< map_location > &locations) const
Definition: halo.cpp:288
effect(display *screen, int xpos, int ypos, const animated< image::locator >::anim_description &img, const map_location &loc, ORIENTATION, bool infinite)
Definition: halo.cpp:143
Animate units.
ORIENTATION orientation_
Definition: halo.cpp:60
bool valid() const
Definition: halo.hpp:88
const T & get_current_frame() const
boost::shared_ptr< halo_impl > impl_
Definition: halo.hpp:75
const image::locator & current_image()
Definition: halo.cpp:56
map_display and display: classes which take care of displaying the map and game-data on the screen...
bool need_update() const
void render()
Definition: halo.cpp:442
void remove(int handle)
Remove the halo with the given handle.
Definition: halo.cpp:353
GLsizei const GLint * locations
Definition: glew.h:11075
surface flop_surface(const surface &surf, bool optimize)
Definition: utils.cpp:2137
handle add(int x, int y, const std::string &image, const map_location &loc, halo::ORIENTATION orientation=NORMAL, bool infinite=true)
Add a haloing effect using 'image centered on (x,y).
Definition: halo.cpp:478
Encapsulates the map of the game.
Definition: location.hpp:38
int add(int x, int y, const std::string &image, const map_location &loc, ORIENTATION orientation=NORMAL, bool infinite=true)
Definition: halo.cpp:315
bool location_not_known() const
Definition: halo.cpp:299
surface & get_screen_surface()
return the screen surface or the surface used for map_screenshot.
Definition: display.hpp:205
bool expired() const
Definition: halo.cpp:47
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.hpp:349
std::set< int > changing_haloes
Haloes that have an animation or expiration time need to be checked every frame and are stored in thi...
Definition: halo.cpp:105
pump_impl & impl_
Definition: pump.cpp:134
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
size_t i
Definition: function.cpp:1057
void unrender(std::set< map_location > invalidated_locations)
Render and unrender haloes.
Definition: halo.cpp:504
void set_location(int x, int y)
Definition: halo.cpp:164
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
bool does_change() const
Definition: halo.cpp:49
tformula< unsigned > y_
The y coordinate of the rectangle.
Definition: canvas.cpp:682
bool animation_finished() const
Returns true if the current animation was finished.
void set_location(const handle &h, int x, int y)
Set the position of an existing haloing effect, according to its handle.
Definition: halo.cpp:486
const rect_of_hexes hexes_under_rect(const SDL_Rect &r) const
Return the rectangular area of hexes overlapped by r (r is in screen coordinates) ...
Definition: display.cpp:666
void render()
Definition: halo.cpp:509
std::set< int > invalidated_haloes
Upon unrendering, an invalidation list is send.
Definition: halo.cpp:85
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 need_update() const
Definition: halo.cpp:48
Definition: display.hpp:43
ORIENTATION
Definition: halo.hpp:38
GLint GLvoid * img
Definition: glew.h:1353
this module manages the cache of images.
Definition: image.cpp:75
const SDL_Rect & map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.hpp:248
bool does_not_change() const
Definition: animated.hpp:95
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
#define e
void sdl_copy_portion(const surface &screen, SDL_Rect *screen_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:116
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:112
RAII object which manages a halo.
Definition: halo.hpp:81
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
int hex_size() const
Function which returns the size of a hex in pixels (from top tip to bottom tip or left edge to right ...
Definition: display.hpp:266
void set_location(int handle, int x, int y)
Set the position of an existing haloing effect, according to its handle.
Definition: halo.cpp:345
void update_rect(const SDL_Rect &)
Definition: dummy_video.cpp:27
iterator begin() const
Definition: display.cpp:657
std::vector< std::string > square_parenthetical_split(std::string const &val, const char separator, std::string const &left, std::string const &right, const int flags)
Similar to parenthetical_split, but also expands embedded square brackets.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
display * disp
Definition: halo.cpp:75
boost::shared_ptr< halo_record > handle
Definition: halo.hpp:34
void start_animation(int start_time, bool cycles=false)
Starts an animation cycle.
void remove(const handle &h)
Remove the halo with the given handle.
Definition: halo.cpp:492