halo.cpp

Go to the documentation of this file.
00001 /* $Id: halo.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Maintain halo-effects for units and items.
00019  * Examples: white mage, lighthouse.
00020  */
00021 
00022 #include "global.hpp"
00023 #include "animated.hpp"
00024 #include "display.hpp"
00025 #include "game_preferences.hpp"
00026 #include "halo.hpp"
00027 #include "serialization/string_utils.hpp"
00028 
00029 namespace halo
00030 {
00031 
00032 namespace {
00033 display* disp = NULL;
00034 
00035 class effect
00036 {
00037 public:
00038     effect(int xpos, int ypos, const animated<image::locator>::anim_description& img,
00039             const map_location& loc, ORIENTATION, bool infinite);
00040 
00041     void set_location(int x, int y);
00042 
00043     bool render();
00044     void unrender();
00045 
00046     bool expired()     const { return !images_.cycles() && images_.animation_finished(); }
00047     bool need_update() const { return images_.need_update(); }
00048     bool does_change() const { return !images_.does_not_change(); }
00049     bool on_location(const std::set<map_location>& locations) const;
00050 
00051     void add_overlay_location(std::set<map_location>& locations);
00052 private:
00053 
00054     const image::locator& current_image() { return images_.get_current_frame(); }
00055 
00056     animated<image::locator> images_;
00057 
00058     ORIENTATION orientation_;
00059 
00060     int x_, y_;
00061     surface surf_, buffer_;
00062     SDL_Rect rect_;
00063 
00064     /** The location of the center of the halo. */
00065     map_location loc_;
00066 
00067     /** All locations over which the halo lies. */
00068     std::vector<map_location> overlayed_hexes_;
00069 };
00070 
00071 std::map<int, effect> haloes;
00072 int halo_id = 1;
00073 
00074 /**
00075  * Upon unrendering, an invalidation list is send. All haloes in that area and
00076  * the other invalidated haloes are stored in this set. Then there'll be
00077  * tested which haloes overlap and they're also stored in this set.
00078  */
00079 std::set<int> invalidated_haloes;
00080 
00081 /**
00082  * A newly added halo will be added to this list, these haloes don't need to be
00083  * unrendered but do not to be rendered regardless which tiles are invalidated.
00084  * These haloes will stay in this set until there're really rendered (rendering
00085  * won't happen if for example the halo is offscreen).
00086  */
00087 std::set<int> new_haloes;
00088 
00089 /**
00090  * Upon deleting, a halo isn't deleted but added to this set, upon unrendering
00091  * the image is unrendered and deleted.
00092  */
00093 std::set<int> deleted_haloes;
00094 
00095 /**
00096  * Haloes that have an animation or expiration time need to be checked every
00097  * frame and are stored in this set.
00098  */
00099 std::set<int> changing_haloes;
00100 
00101 effect::effect(int xpos, int ypos, const animated<image::locator>::anim_description& img,
00102         const map_location& loc, ORIENTATION orientation, bool infinite) :
00103     images_(img),
00104     orientation_(orientation),
00105     x_(xpos),
00106     y_(ypos),
00107     surf_(NULL),
00108     buffer_(NULL),
00109     rect_(empty_rect),
00110     loc_(loc),
00111     overlayed_hexes_()
00112 {
00113     assert(disp != NULL);
00114 
00115     set_location(xpos,ypos);
00116 
00117     images_.start_animation(0,infinite);
00118 
00119 }
00120 
00121 void effect::set_location(int x, int y)
00122 {
00123     const map_location zero_loc(0,0);
00124     int new_x = x - disp->get_location_x(zero_loc);
00125     int new_y = y - disp->get_location_y(zero_loc);
00126     if (new_x != x_ || new_y != y_) {
00127         x_ = new_x;
00128         y_ = new_y;
00129         buffer_.assign(NULL);
00130         overlayed_hexes_.clear();
00131     }
00132 }
00133 
00134 bool effect::render()
00135 {
00136     if(disp == NULL) {
00137         return false;
00138     }
00139 
00140     if(loc_.x != -1 && loc_.y != -1) {
00141         if(disp->shrouded(loc_)) {
00142             return false;
00143         } else {
00144             // The location of a halo is an x,y value and not a map location.
00145             // This means when a map is zoomed, the halo's won't move,
00146             // This glitch is most visible on [item] haloes.
00147             // This workaround always recalculates the location of the halo
00148             // (item haloes have a location parameter to hide them under the shroud)
00149             // and reapplies that location.
00150             // It might be optimized by storing and comparing the zoom value.
00151             set_location(
00152                 disp->get_location_x(loc_) + disp->hex_size() / 2,
00153                 disp->get_location_y(loc_) + disp->hex_size() / 2);
00154         }
00155     }
00156 
00157     images_.update_last_draw_time();
00158     surf_.assign(image::get_image(current_image(),image::SCALED_TO_ZOOM));
00159     if(surf_ == NULL) {
00160         return false;
00161     }
00162     if(orientation_ == HREVERSE || orientation_ == HVREVERSE) {
00163         surf_.assign(image::reverse_image(surf_));
00164     }
00165     if(orientation_ == VREVERSE || orientation_ == HVREVERSE) {
00166         surf_.assign(flop_surface(surf_));
00167     }
00168 
00169     const map_location zero_loc(0,0);
00170     const int screenx = disp->get_location_x(zero_loc);
00171     const int screeny = disp->get_location_y(zero_loc);
00172 
00173     const int xpos = x_ + screenx - surf_->w/2;
00174     const int ypos = y_ + screeny - surf_->h/2;
00175 
00176     SDL_Rect rect = create_rect(xpos, ypos, surf_->w, surf_->h);
00177     rect_ = rect;
00178     SDL_Rect clip_rect = disp->map_outside_area();
00179 
00180     // If rendered the first time, need to determine the area affected.
00181     // If a halo changes size, it is not updated.
00182     if(overlayed_hexes_.empty()) {
00183         display::rect_of_hexes hexes = disp->hexes_under_rect(rect);
00184         display::rect_of_hexes::iterator i = hexes.begin(), end = hexes.end();
00185         for (;i != end; ++i) {
00186             overlayed_hexes_.push_back(*i);
00187         }
00188     }
00189 
00190     if(rects_overlap(rect,clip_rect) == false) {
00191         buffer_.assign(NULL);
00192         return false;
00193     }
00194 
00195     surface screen = disp->get_screen_surface();
00196 
00197     const clip_rect_setter clip_setter(screen, &clip_rect);
00198     if(buffer_ == NULL || buffer_->w != rect.w || buffer_->h != rect.h) {
00199         SDL_Rect rect = rect_;
00200         buffer_.assign(get_surface_portion(screen,rect));
00201     } else {
00202         SDL_Rect rect = rect_;
00203         sdl_blit(screen,&rect,buffer_,NULL);
00204     }
00205 
00206     sdl_blit(surf_,NULL,screen,&rect);
00207 
00208     update_rect(rect_);
00209 
00210     return true;
00211 }
00212 
00213 void effect::unrender()
00214 {
00215     if (!surf_ || !buffer_) {
00216         return;
00217     }
00218 
00219     surface screen = disp->get_screen_surface();
00220 
00221     SDL_Rect clip_rect = disp->map_outside_area();
00222     const clip_rect_setter clip_setter(screen, &clip_rect);
00223 
00224     // Due to scrolling, the location of the rendered halo
00225     // might have changed; recalculate
00226     const map_location zero_loc(0,0);
00227     const int screenx = disp->get_location_x(zero_loc);
00228     const int screeny = disp->get_location_y(zero_loc);
00229 
00230     const int xpos = x_ + screenx - surf_->w/2;
00231     const int ypos = y_ + screeny - surf_->h/2;
00232 
00233     SDL_Rect rect = create_rect(xpos, ypos, surf_->w, surf_->h);
00234     sdl_blit(buffer_,NULL,screen,&rect);
00235     update_rect(rect);
00236 }
00237 
00238 bool effect::on_location(const std::set<map_location>& locations) const
00239 {
00240     for(std::vector<map_location>::const_iterator itor = overlayed_hexes_.begin();
00241             itor != overlayed_hexes_.end(); ++itor) {
00242         if(locations.find(*itor) != locations.end()) {
00243             return true;
00244         }
00245     }
00246     return false;
00247 }
00248 
00249 void effect::add_overlay_location(std::set<map_location>& locations)
00250 {
00251     for(std::vector<map_location>::const_iterator itor = overlayed_hexes_.begin();
00252             itor != overlayed_hexes_.end(); ++itor) {
00253 
00254         locations.insert(*itor);
00255     }
00256 }
00257 
00258 } // end anon namespace
00259 
00260 manager::manager(display& screen) : old(disp)
00261 {
00262     disp = &screen;
00263 }
00264 
00265 manager::~manager()
00266 {
00267     haloes.clear();
00268     invalidated_haloes.clear();
00269     new_haloes.clear();
00270     deleted_haloes.clear();
00271     changing_haloes.clear();
00272 
00273     disp = old;
00274 }
00275 
00276 int add(int x, int y, const std::string& image, const map_location& loc,
00277         ORIENTATION orientation, bool infinite)
00278 {
00279     const int id = halo_id++;
00280     animated<image::locator>::anim_description image_vector;
00281     std::vector<std::string> items = utils::parenthetical_split(image, ',');
00282     std::vector<std::string>::const_iterator itor = items.begin();
00283     for(; itor != items.end(); ++itor) {
00284         const std::vector<std::string>& items = utils::split(*itor, ':');
00285         std::string str;
00286         int time;
00287 
00288         if(items.size() > 1) {
00289             str = items.front();
00290             time = atoi(items.back().c_str());
00291         } else {
00292             str = *itor;
00293             time = 100;
00294         }
00295         image_vector.push_back(animated<image::locator>::frame_description(time,image::locator(str)));
00296 
00297     }
00298     haloes.insert(std::pair<int,effect>(id,effect(x,y,image_vector,loc,orientation,infinite)));
00299     new_haloes.insert(id);
00300     if(haloes.find(id)->second.does_change() || !infinite) {
00301         changing_haloes.insert(id);
00302     }
00303     return id;
00304 }
00305 
00306 void set_location(int handle, int x, int y)
00307 {
00308     const std::map<int,effect>::iterator itor = haloes.find(handle);
00309     if(itor != haloes.end()) {
00310         itor->second.set_location(x,y);
00311     }
00312 }
00313 
00314 void remove(int handle)
00315 {
00316     // Silently ignore invalid haloes.
00317     // This happens when Wesnoth is being terminated as well.
00318     if(handle == NO_HALO || haloes.find(handle) == haloes.end())  {
00319         return;
00320     }
00321 
00322     deleted_haloes.insert(handle);
00323 }
00324 
00325 void unrender(std::set<map_location> invalidated_locations)
00326 {
00327     if(preferences::show_haloes() == false || haloes.size() == 0) {
00328         return;
00329     }
00330     //assert(invalidated_haloes.size() == 0);
00331 
00332     // Remove expired haloes
00333     std::map<int, effect>::iterator itor = haloes.begin();
00334     for(; itor != haloes.end(); ++itor ) {
00335         if(itor->second.expired()) {
00336             deleted_haloes.insert(itor->first);
00337         }
00338     }
00339 
00340     // Add the haloes marked for deletion to the invalidation set
00341     std::set<int>::const_iterator set_itor = deleted_haloes.begin();
00342     for(;set_itor != deleted_haloes.end(); ++set_itor) {
00343         invalidated_haloes.insert(*set_itor);
00344         haloes.find(*set_itor)->second.add_overlay_location(invalidated_locations);
00345     }
00346 
00347     // Test the multi-frame haloes whether they need an update
00348     for(set_itor = changing_haloes.begin();
00349             set_itor != changing_haloes.end(); ++set_itor) {
00350         if(haloes.find(*set_itor)->second.need_update()) {
00351             invalidated_haloes.insert(*set_itor);
00352             haloes.find(*set_itor)->second.add_overlay_location(invalidated_locations);
00353         }
00354     }
00355 
00356     // Find all halo's in a the invalidated area
00357     size_t halo_count;
00358 
00359     // Repeat until set of haloes in the invalidated area didn't change
00360     // (including none found) or all existing haloes are found.
00361     do {
00362         halo_count = invalidated_haloes.size();
00363         for(itor = haloes.begin(); itor != haloes.end(); ++itor) {
00364             // Test all haloes not yet in the set
00365             // which match one of the locations
00366             if(invalidated_haloes.find(itor->first) == invalidated_haloes.end() &&
00367                     itor->second.on_location(invalidated_locations)) {
00368 
00369                 // If found, add all locations which the halo invalidates,
00370                 // and add it to the set
00371                 itor->second.add_overlay_location(invalidated_locations);
00372                 invalidated_haloes.insert(itor->first);
00373             }
00374         }
00375     } while (halo_count != invalidated_haloes.size() && halo_count != haloes.size());
00376 
00377     if(halo_count == 0) {
00378         return;
00379     }
00380 
00381     // Render the haloes:
00382     // iterate through all the haloes and invalidate if in set
00383     for(std::map<int, effect>::reverse_iterator ritor = haloes.rbegin(); ritor != haloes.rend(); ++ritor) {
00384         if(invalidated_haloes.find(ritor->first) != invalidated_haloes.end()) {
00385             ritor->second.unrender();
00386         }
00387     }
00388 
00389     // Really delete the haloes marked for deletion
00390     for(set_itor = deleted_haloes.begin(); set_itor != deleted_haloes.end(); ++set_itor) {
00391         // It can happen a deleted halo hasn't been rendered yet, invalidate them as well
00392         new_haloes.erase(*set_itor);
00393 
00394         changing_haloes.erase(*set_itor);
00395         invalidated_haloes.erase(*set_itor);
00396         haloes.erase(*set_itor);
00397     }
00398 
00399     deleted_haloes.clear();
00400 }
00401 
00402 void render()
00403 {
00404     if(preferences::show_haloes() == false || haloes.size() == 0 ||
00405             (new_haloes.size() == 0 && invalidated_haloes.size() == 0)) {
00406         return;
00407     }
00408 
00409     // Keep track of not rendered new images they have to be kept scheduled
00410     // for rendering otherwise the invalidation area is never properly set
00411     std::set<int> unrendered_new_haloes;
00412 
00413     // Render the haloes:
00414     // iterate through all the haloes and draw if in either set
00415     for(std::map<int, effect>::iterator itor = haloes.begin();
00416             itor != haloes.end(); ++itor) {
00417 
00418         if(new_haloes.find(itor->first) != new_haloes.end() &&
00419                 ! itor->second.render()) {
00420 
00421             unrendered_new_haloes.insert(itor->first);
00422         } else if(invalidated_haloes.find(itor->first) != invalidated_haloes.end()) {
00423             itor->second.render();
00424         }
00425     }
00426 
00427     invalidated_haloes.clear();
00428     new_haloes = unrendered_new_haloes;
00429 }
00430 
00431 } // end namespace halo
00432 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:00 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs