unit_frame.cpp

Go to the documentation of this file.
00001 /* $Id: unit_frame.cpp 26776 2008-05-22 16:32:19Z mordante $ */
00002 /*
00003    Copyright (C) 2006 - 2008 by Jeremy Rosen <jeremy.rosen@enst-bretagne.fr>
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 version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 /** @file unit_frame.cpp */
00016 
00017 #include "global.hpp"
00018 #include "sound.hpp"
00019 #include "halo.hpp"
00020 #include <cassert>
00021 // NOTE: global.hpp must be first!
00022 
00023 #include "display.hpp"
00024 #define UNIT_FRAME_H_PART2
00025 #include "unit_frame.hpp"
00026 
00027 
00028 progressive_string::progressive_string(const std::string & data,int duration)
00029 {
00030         const std::vector<std::string> first_pass = utils::split(data);
00031         input_ = data;
00032         const int time_chunk = maximum<int>(duration / (first_pass.size()?first_pass.size():1),1);
00033 
00034         std::vector<std::string>::const_iterator tmp;
00035         for(tmp=first_pass.begin();tmp != first_pass.end() ; tmp++) {
00036             std::vector<std::string> second_pass = utils::split(*tmp,':');
00037             if(second_pass.size() > 1) {
00038                 data_.push_back(std::pair<std::string,int>(second_pass[0],atoi(second_pass[1].c_str())));
00039             } else {
00040                 data_.push_back(std::pair<std::string,int>(second_pass[0],time_chunk));
00041             }
00042         }
00043 }
00044 int progressive_string::duration() const
00045 {
00046     int total =0;
00047     std::vector<std::pair<std::string,int> >::const_iterator cur_halo;
00048     for(cur_halo = data_.begin() ; cur_halo != data_.end() ; cur_halo++) {
00049         total += cur_halo->second;
00050     }
00051     return total;
00052 
00053 }
00054 static const std::string empty_string ="";
00055 const std::string& progressive_string::get_current_element(int current_time)const
00056 {
00057     int time = 0;
00058     unsigned int sub_halo = 0;
00059     if(data_.empty()) return empty_string;
00060     while(time < current_time&& sub_halo < data_.size()) {
00061         time += data_[sub_halo].second;
00062         sub_halo++;
00063 
00064     }
00065     if(sub_halo) sub_halo--;
00066     if(sub_halo >= data_.size()) sub_halo = data_.size();
00067     return data_[sub_halo].first;
00068 }
00069 
00070 template <class T>
00071 progressive_<T>::progressive_(const std::string &data, int duration)
00072 {
00073     input_ = data;
00074     const std::vector<std::string> first_split = utils::split(data);
00075     const int time_chunk = maximum<int>(duration / (first_split.size()?first_split.size():1),1);
00076 
00077     std::vector<std::string>::const_iterator tmp;
00078     std::vector<std::pair<std::string,int> > first_pass;
00079     for(tmp=first_split.begin();tmp != first_split.end() ; tmp++) {
00080         std::vector<std::string> second_pass = utils::split(*tmp,':');
00081         if(second_pass.size() > 1) {
00082             first_pass.push_back(std::pair<std::string,int>(second_pass[0],atoi(second_pass[1].c_str())));
00083         } else {
00084             first_pass.push_back(std::pair<std::string,int>(second_pass[0],time_chunk));
00085         }
00086     }
00087     std::vector<std::pair<std::string,int> >::const_iterator tmp2;
00088     for(tmp2=first_pass.begin();tmp2 != first_pass.end() ; tmp2++) {
00089         std::vector<std::string> range = utils::split(tmp2->first,'~');
00090         data_.push_back(std::pair<std::pair<T, T>,int> (
00091             std::pair<T, T>(
00092                 lexical_cast<T>(range[0].c_str()),
00093                 lexical_cast<T>(range.size() > 1 ? range[1].c_str() : range[0].c_str())),
00094                 tmp2->second));
00095     }
00096 
00097 }
00098 
00099 template <class T>
00100 const T progressive_<T>::get_current_element(int current_time, T default_val) const
00101 {
00102     int time = 0;
00103     unsigned int sub_halo = 0;
00104     int searched_time = current_time;
00105     if(searched_time < 0) searched_time = 0;
00106     if(searched_time > duration()) searched_time = duration();
00107     if(data_.empty()) return default_val;
00108     while(time < searched_time&& sub_halo < data_.size()) {
00109         time += data_[sub_halo].second;
00110         sub_halo++;
00111 
00112     }
00113     if(sub_halo != 0) {
00114         sub_halo--;
00115         time -= data_[sub_halo].second;
00116     }
00117     if(sub_halo >= data_.size()) {
00118         sub_halo = data_.size();
00119         time = searched_time; // Never more than max allowed
00120     }
00121 
00122     const T first =  data_[sub_halo].first.first;
00123     const T second =  data_[sub_halo].first.second;
00124 
00125     return T((static_cast<double>(searched_time - time) /
00126         static_cast<double>(data_[sub_halo].second)) *
00127         (second - first) + first);
00128 }
00129 
00130 template<class T>
00131 int progressive_<T>::duration() const
00132 {
00133     int total = 0;
00134     typename std::vector<std::pair<std::pair<T, T>, int> >::const_iterator cur_halo;
00135     for(cur_halo = data_.begin() ; cur_halo != data_.end() ; cur_halo++) {
00136         total += cur_halo->second;
00137     }
00138     return total;
00139 
00140 }
00141 
00142 template <class T>
00143 bool progressive_<T>::does_not_change() const
00144 {
00145 return data_.empty() ||
00146     ( data_.size() == 1 && data_[0].first.first == data_[0].first.second);
00147 }
00148 
00149 // Force compilation of the following template instantiations
00150 template class progressive_<int>;
00151 template class progressive_<double>;
00152 
00153 #undef UNIT_FRAME_H_PART2
00154 #include "unit_frame.hpp"
00155 
00156 
00157 
00158 frame_builder::frame_builder(const config& cfg,const std::string& frame_string)
00159 {
00160     image(image::locator(cfg[frame_string+"image"]),cfg[frame_string+"image_mod"]);
00161     image_diagonal(image::locator(cfg[frame_string+"image_diagonal"]),cfg[frame_string+"image_mod"]);
00162     sound(cfg[frame_string+"sound"]);
00163     std::vector<std::string> tmp_string_vect=utils::split(cfg[frame_string+"text_color"]);
00164     if(tmp_string_vect.size() ==3) {
00165         text(cfg[frame_string+"text"],
00166          display::rgb(atoi(tmp_string_vect[0].c_str()),atoi(tmp_string_vect[1].c_str()),atoi(tmp_string_vect[2].c_str())));
00167     } else {
00168         text(cfg[frame_string+"text"],0);
00169     }
00170 
00171     if(!cfg[frame_string+"duration"].empty()) {
00172         duration(atoi(cfg[frame_string+"duration"].c_str()));
00173     } else {
00174         duration(atoi(cfg[frame_string+"end"].c_str()) - atoi(cfg[frame_string+"begin"].c_str()));
00175     }
00176     halo(cfg[frame_string+"halo"],cfg[frame_string+"halo_x"],cfg[frame_string+"halo_y"]);
00177      tmp_string_vect=utils::split(cfg[frame_string+"blend_color"]);
00178     if(tmp_string_vect.size() ==3) {
00179         blend(cfg[frame_string+"blend_ratio"],display::rgb(atoi(tmp_string_vect[0].c_str()),atoi(tmp_string_vect[1].c_str()),atoi(tmp_string_vect[2].c_str())));
00180     } else {
00181         blend(cfg[frame_string+"blend_ratio"],0);
00182     }
00183     highlight(cfg[frame_string+"alpha"]);
00184     offset(cfg[frame_string+"offset"]);
00185     submerge(cfg[frame_string+"submerge"]);
00186     x(cfg[frame_string+"x"]);
00187     y(cfg[frame_string+"y"]);
00188 
00189 }
00190 
00191 const frame_parameters frame_builder::parameters(int current_time) const
00192 {
00193     frame_parameters result;
00194     result.duration = duration_;
00195     result.image = image_;
00196     result.image_diagonal = image_diagonal_;
00197     result.image_mod = image_mod_;
00198     result.halo = halo_.get_current_element(current_time);
00199     result.halo_x = halo_x_.get_current_element(current_time);
00200     result.halo_y = halo_y_.get_current_element(current_time);
00201     result.sound = sound_;
00202     result.text = text_;
00203     result.text_color = text_color_;
00204     result.blend_with = blend_with_;
00205     result.blend_ratio = blend_ratio_.get_current_element(current_time);
00206     result.highlight_ratio = highlight_ratio_.get_current_element(current_time,1.0);
00207     result.offset = offset_.get_current_element(current_time);
00208     result.submerge = submerge_.get_current_element(current_time);
00209     result.x = x_.get_current_element(current_time);
00210     result.y = y_.get_current_element(current_time);
00211     return result;
00212 }
00213 frame_builder & frame_builder::image(const image::locator image ,const std::string & image_mod)
00214 {
00215     image_ = image;
00216     image_mod_ = image_mod;
00217     return *this;
00218 }
00219 frame_builder & frame_builder::image_diagonal(const image::locator image_diagonal,const std::string& image_mod)
00220 {
00221     image_diagonal_ = image_diagonal;
00222     image_mod_ = image_mod;
00223     return *this;
00224 }
00225 frame_builder & frame_builder::sound(const std::string& sound)
00226 {
00227     sound_=sound;
00228     return *this;
00229 }
00230 frame_builder & frame_builder::text(const std::string& text,const  Uint32 text_color)
00231 {
00232     text_=text;
00233     text_color_=text_color;
00234     return *this;
00235 }
00236 frame_builder & frame_builder::halo(const std::string &halo, const std::string &halo_x, const std::string& halo_y)
00237 {
00238     halo_ = progressive_string(halo,duration_);
00239     halo_x_ = progressive_int(halo_x,duration_);
00240     halo_y_ = progressive_int(halo_y,duration_);
00241     return *this;
00242 }
00243 frame_builder & frame_builder::duration(const int duration)
00244 {
00245     duration_= duration;
00246     recalculate_duration();
00247     return *this;
00248 }
00249 void frame_builder::recalculate_duration()
00250 {
00251     halo_ = progressive_string(halo_.get_original(),duration_);
00252     halo_x_ = progressive_int(halo_x_.get_original(),duration_);
00253     halo_y_ = progressive_int(halo_y_.get_original(),duration_);
00254     blend_ratio_=progressive_double(blend_ratio_.get_original(),duration_);
00255     highlight_ratio_=progressive_double(highlight_ratio_.get_original(),duration_);
00256     offset_=progressive_double(offset_.get_original(),duration_);
00257     submerge_=progressive_double(submerge_.get_original(),duration_);
00258     x_=progressive_int(x_.get_original(),duration_);
00259     y_=progressive_int(y_.get_original(),duration_);
00260 }
00261 frame_builder & frame_builder::blend(const std::string& blend_ratio,const Uint32 blend_color)
00262 {
00263     blend_with_=blend_color;
00264     blend_ratio_=progressive_double(blend_ratio,duration_);
00265     return *this;
00266 }
00267 frame_builder & frame_builder::highlight(const std::string& highlight)
00268 {
00269     highlight_ratio_=progressive_double(highlight,duration_);
00270     return *this;
00271 }
00272 frame_builder & frame_builder::offset(const std::string& offset)
00273 {
00274     offset_=progressive_double(offset,duration_);
00275     return *this;
00276 }
00277 frame_builder & frame_builder::submerge(const std::string& submerge)
00278 {
00279     submerge_=progressive_double(submerge,duration_);
00280     return *this;
00281 }
00282 frame_builder & frame_builder::x(const std::string& x)
00283 {
00284     x_=progressive_int(x,duration_);
00285     return *this;
00286 }
00287 frame_builder & frame_builder::y(const std::string& y)
00288 {
00289     y_=progressive_int(y,duration_);
00290     return *this;
00291 }
00292 bool frame_builder::does_not_change() const
00293 {
00294     return halo_.does_not_change() &&
00295         halo_x_.does_not_change() &&
00296         halo_y_.does_not_change() &&
00297         blend_ratio_.does_not_change() &&
00298         highlight_ratio_.does_not_change() &&
00299         offset_.does_not_change() &&
00300         submerge_.does_not_change() &&
00301         x_.does_not_change() &&
00302         y_.does_not_change();
00303 }
00304 bool frame_builder::need_update() const
00305 {
00306     if(!halo_.does_not_change() ||
00307             !halo_x_.does_not_change() ||
00308             !halo_y_.does_not_change() ||
00309             !blend_ratio_.does_not_change() ||
00310             !highlight_ratio_.does_not_change() ||
00311             !offset_.does_not_change() ||
00312             !submerge_.does_not_change() ||
00313             !x_.does_not_change() ||
00314             !y_.does_not_change() ) {
00315             return true;
00316     }
00317     return false;
00318 }
00319 
00320 void unit_frame::redraw(const int frame_time,bool first_time,const gamemap::location & src,const gamemap::location & dst,int*halo_id,const frame_parameters & animation_val,const frame_parameters & engine_val)const
00321 {
00322     const int xsrc = game_display::get_singleton()->get_location_x(src);
00323     const int ysrc = game_display::get_singleton()->get_location_y(src);
00324     const int xdst = game_display::get_singleton()->get_location_x(dst);
00325     const int ydst = game_display::get_singleton()->get_location_y(dst);
00326     const gamemap::location::DIRECTION direction = src.get_relative_dir(dst);
00327 
00328     const frame_parameters current_data = merge_parameters(frame_time,animation_val,engine_val);
00329     double tmp_offset = current_data.offset;
00330     int d2 = game_display::get_singleton()->hex_size() / 2;
00331 
00332     if(first_time ) {
00333         // stuff sthat should be done only once per frame
00334         if(!current_data.sound.empty()  ) {
00335             sound::play_sound(current_data.sound);
00336         }
00337         if(!current_data.text.empty()  ) {
00338             game_display::get_singleton()->float_label(src,current_data.text,
00339             (current_data.text_color & 0x00FF0000) >> 16,
00340             (current_data.text_color & 0x0000FF00) >> 8,
00341             (current_data.text_color & 0x000000FF) >> 0);
00342         }
00343     }
00344     image::locator image_loc;
00345     if(direction != gamemap::location::NORTH && direction != gamemap::location::SOUTH) {
00346         image_loc = image::locator(current_data.image_diagonal,current_data.image_mod);
00347     } 
00348     if(image_loc.is_void() || image_loc.get_filename() == "") { // invalid diag image, or not diagonal
00349         image_loc = image::locator(current_data.image,current_data.image_mod);
00350     }
00351 
00352     surface image;
00353     if(!image_loc.is_void() && image_loc.get_filename() != "") { // invalid diag image, or not diagonal
00354         image=image::get_image(image_loc,
00355                 image::SCALED_TO_ZOOM,
00356                 false
00357                 );
00358     }
00359     const int x = static_cast<int>(tmp_offset * xdst + (1.0-tmp_offset) * xsrc) + d2;
00360     const int y = static_cast<int>(tmp_offset * ydst + (1.0-tmp_offset) * ysrc) + d2;
00361     if (image != NULL) {
00362         bool facing_west = direction == gamemap::location::NORTH_WEST || direction == gamemap::location::SOUTH_WEST;
00363         bool facing_north = direction == gamemap::location::NORTH_WEST || direction == gamemap::location::NORTH || direction == gamemap::location::NORTH_EAST;
00364         game_display::get_singleton()->render_unit_image(x + current_data.x- image->w/2,
00365                     y  + current_data.y- image->h/2, false,
00366                     gamemap::get_drawing_order(src), image, facing_west, false,
00367                 ftofxp(current_data.highlight_ratio), current_data.blend_with,
00368                     current_data.blend_ratio,current_data.submerge,!facing_north);
00369     }
00370     halo::remove(*halo_id);
00371     *halo_id = halo::NO_HALO;
00372     if(!current_data.halo.empty()) {
00373         halo::ORIENTATION orientation;
00374         switch(direction)
00375         {
00376             case gamemap::location::NORTH:
00377             case gamemap::location::NORTH_EAST:
00378                 orientation = halo::NORMAL;
00379                 break;
00380             case gamemap::location::SOUTH_EAST:
00381             case gamemap::location::SOUTH:
00382                 orientation = halo::VREVERSE;
00383                 break;
00384             case gamemap::location::SOUTH_WEST:
00385                 orientation = halo::HVREVERSE;
00386                 break;
00387             case gamemap::location::NORTH_WEST:
00388                 orientation = halo::HREVERSE;
00389                 break;
00390             case gamemap::location::NDIRECTIONS:
00391             default:
00392                 orientation = halo::NORMAL;
00393                 break;
00394         }
00395         if(direction != gamemap::location::SOUTH_WEST && direction != gamemap::location::NORTH_WEST) {
00396             *halo_id = halo::add(static_cast<int>(x+current_data.halo_x* game_display::get_singleton()->get_zoom_factor()),
00397                     static_cast<int>(y+current_data.halo_y* game_display::get_singleton()->get_zoom_factor()),
00398                     current_data.halo,
00399                     gamemap::location(-1, -1),
00400                     orientation);
00401         } else {
00402             *halo_id = halo::add(static_cast<int>(x-current_data.halo_x* game_display::get_singleton()->get_zoom_factor()),
00403                     static_cast<int>(y+current_data.halo_y* game_display::get_singleton()->get_zoom_factor()),
00404                     current_data.halo,
00405                     gamemap::location(-1, -1),
00406                     orientation);
00407         }
00408     }
00409 }
00410 void unit_frame::invalidate(const int frame_time,const gamemap::location & src,const gamemap::location & dst,const frame_parameters & animation_val,const frame_parameters & engine_val,const bool primary) const
00411 {
00412     const int xsrc = game_display::get_singleton()->get_location_x(src);
00413     const int ysrc = game_display::get_singleton()->get_location_y(src);
00414     const int xdst = game_display::get_singleton()->get_location_x(dst);
00415     const int ydst = game_display::get_singleton()->get_location_y(dst);
00416     const gamemap::location::DIRECTION direction = src.get_relative_dir(dst);
00417 
00418     const frame_parameters current_data = merge_parameters(frame_time,animation_val,engine_val,primary);
00419     double tmp_offset = current_data.offset;
00420     //unused var - int d2 = game_display::get_singleton()->hex_size() / 2;
00421 
00422     image::locator image_loc;
00423     if(direction != gamemap::location::NORTH && direction != gamemap::location::SOUTH) {
00424         image_loc = current_data.image_diagonal;
00425     } 
00426     if(image_loc.is_void() || image_loc.get_filename() == "") { // invalid diag image, or not diagonal
00427         image_loc = current_data.image;
00428     }
00429 
00430     surface image;
00431     if(!image_loc.is_void() && image_loc.get_filename() != "") { // invalid diag image, or not diagonal
00432         /**
00433          * @todo cache handling: here we will use the image again soon we
00434          * should cache it here and release it (if needed) after redrawn
00435          */
00436         image=image::get_image(image_loc,
00437                 image::SCALED_TO_ZOOM,
00438                 false
00439                 );
00440     }
00441     const int x = static_cast<int>(tmp_offset * xdst + (1.0-tmp_offset) * xsrc)+current_data.x;
00442     const int y = static_cast<int>(tmp_offset * ydst + (1.0-tmp_offset) * ysrc)+current_data.y;
00443     if (image != NULL) {
00444         // optimization for most common case
00445         if(x==xsrc && y == ysrc && 
00446                 image->w  == game_display::get_singleton()->hex_size() &&
00447                 image->h  == game_display::get_singleton()->hex_size()) {
00448 
00449             game_display::get_singleton()->invalidate(src);
00450         } else {
00451             game_display::get_singleton()->invalidate_zone(x,y,x+image->w,y+image->h);
00452         }
00453 
00454     }
00455 }
00456 
00457 
00458 
00459 const frame_parameters unit_frame::merge_parameters(int current_time,const frame_parameters & animation_val,const frame_parameters & engine_val, bool primary) const
00460 {
00461     /** 
00462      * this function merges the value provided by
00463      *  * the frame
00464      *  * the engine (poison, flying unit...)
00465      *  * the animation as a whole
00466      *  there is no absolute rule for merging, so creativity is the rule
00467      *  if a value is never provided by the engine, assert. (this way if it becomes used, people will easily find the right place to look)
00468      *  
00469      */
00470     frame_parameters result;
00471     const frame_parameters & current_val = builder_.parameters(current_time);
00472     assert(engine_val.image.is_void() || engine_val.image.get_filename() == "");
00473     result.image = current_val.image.is_void() || current_val.image.get_filename() == ""?animation_val.image:current_val.image;
00474 
00475     assert(engine_val.image_diagonal.is_void() || engine_val.image_diagonal.get_filename() == "");
00476     result.image_diagonal = current_val.image_diagonal.is_void()|| current_val.image_diagonal.get_filename() == ""?animation_val.image_diagonal:current_val.image_diagonal;
00477 
00478     result.image_mod = engine_val.image_mod + current_val.image_mod +animation_val.image_mod;
00479 
00480     assert(engine_val.halo.empty());
00481     result.halo = current_val.halo.empty()?animation_val.halo:current_val.halo;
00482 
00483     assert(engine_val.halo_x == 0);
00484     result.halo_x =  current_val.halo_x?current_val.halo_x:animation_val.halo_x;
00485 
00486     result.halo_y = current_val.halo_y?current_val.halo_y:animation_val.halo_y;
00487     result.halo_y += engine_val.halo_y;
00488 
00489     assert(engine_val.duration == 0);
00490     result.duration = current_val.duration;
00491 
00492     assert(engine_val.sound.empty());
00493     result.sound = current_val.sound.empty()?animation_val.sound:current_val.sound;
00494 
00495     assert(engine_val.text.empty());
00496     result.text = current_val.text.empty()?animation_val.text:current_val.text;
00497 
00498     assert(engine_val.text_color == 0);
00499     result.text_color = current_val.text_color?current_val.text_color:animation_val.text_color;
00500 
00501     result.blend_with = current_val.blend_with?current_val.blend_with:animation_val.blend_with;
00502     if(primary&& engine_val.blend_with) result.blend_with = engine_val.blend_with;
00503 
00504     result.blend_ratio = current_val.blend_ratio?current_val.blend_ratio:animation_val.blend_ratio;
00505     if(primary && engine_val.blend_ratio) result.blend_ratio += engine_val.blend_ratio;
00506 
00507     result.highlight_ratio = current_val.highlight_ratio!=1.0?current_val.highlight_ratio:animation_val.highlight_ratio;
00508     if(primary && engine_val.highlight_ratio != 1.0) result.highlight_ratio = result.highlight_ratio +engine_val.highlight_ratio - 1.0; // selected unit
00509 
00510     assert(engine_val.offset == 0);
00511     result.offset = current_val.offset?current_val.offset:animation_val.offset;
00512 
00513     result.submerge = current_val.submerge?current_val.submerge:animation_val.submerge;
00514     if(primary && engine_val.submerge) result.submerge = engine_val.submerge;
00515 
00516     assert(engine_val.x == 0);
00517     result.x = current_val.x?current_val.x:animation_val.x;
00518 
00519     result.y = current_val.y?current_val.y:animation_val.y; 
00520     result.y += engine_val.y;
00521 
00522     return result;
00523 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs