diff --git a/changelog b/changelog index 99e33dc61dc..212de1b9390 100644 --- a/changelog +++ b/changelog @@ -26,6 +26,8 @@ Version 1.3.12+svn: full heal (+25% max xp) * gave the Necrophage a feeding ability, giving it +1 max HP for every living enemy killed + * a floating text can now be specified within animation frames using the + text= and text_color= keys * sound: * new or improved sounds: ogre hit and die * User interface diff --git a/src/animated.hpp b/src/animated.hpp index 06c4a436634..2dea22e59a8 100644 --- a/src/animated.hpp +++ b/src/animated.hpp @@ -83,7 +83,11 @@ public: static const T void_value_; //MSVC: the frame constructor below requires this to be public protected: +friend class unit_animation; int starting_frame_time_; + // backward compatibility for teleport anims + void remove_frames_until(int starting_time); + void remove_frames_after(int ending_time); private: struct frame diff --git a/src/animated.i b/src/animated.i index a47e9c570e0..3b967afe853 100644 --- a/src/animated.i +++ b/src/animated.i @@ -284,4 +284,26 @@ int animated::get_end_time() const return starting_frame_time_; return frames_.back().start_time_ + frames_.back().duration_; } +template +void animated::remove_frames_until(int new_starting_time) +{ + while (starting_frame_time_ < new_starting_time && !frames_.empty() ) { + starting_frame_time_ += frames_[0].duration_; + frames_.erase(frames_.begin()); + } + +} +template +void animated::remove_frames_after(int new_ending_time) +{ +int last_start_time = starting_frame_time_; +typename std::vector::iterator current_frame = frames_.begin(); + while (last_start_time < new_ending_time && current_frame != frames_.end()) { + last_start_time += current_frame->duration_; + current_frame++; + } + frames_.erase(current_frame,frames_.end()); + +} + diff --git a/src/dialogs.cpp b/src/dialogs.cpp index 64461578eaa..8ea50dab4a4 100644 --- a/src/dialogs.cpp +++ b/src/dialogs.cpp @@ -159,13 +159,10 @@ bool animate_unit_advancement(const game_data& info,unit_map& units, gamemap::lo // to the new unit, then fades back to the normal colour if(!gui.video().update_locked()) { - u->second.set_leveling_out(gui,u->first); - while(!u->second.get_animation()->animation_would_finish()) { - gui.invalidate(loc); - gui.draw(); - events::pump(); - gui.delay(10); - } + unit_animator animator; + animator.add_animation(&u->second,"levelout",u->first); + animator.start_animations(); + animator.wait_for_end(); } if(choice < options.size()) { @@ -189,13 +186,10 @@ bool animate_unit_advancement(const game_data& info,unit_map& units, gamemap::lo gui.invalidate_unit(); if(u != units.end() && !gui.video().update_locked()) { - u->second.set_leveling_in(gui,u->first); - while(!u->second.get_animation()->animation_would_finish()) { - gui.invalidate(loc); - gui.draw(); - events::pump(); - gui.delay(10); - } + unit_animator animator; + animator.add_animation(&u->second,"levelin",u->first); + animator.start_animations(); + animator.wait_for_end(); u->second.set_standing(gui,u->first); gui.invalidate(loc); gui.draw(); diff --git a/src/game_events.cpp b/src/game_events.cpp index 30026f7dd57..79130ccd51b 100644 --- a/src/game_events.cpp +++ b/src/game_events.cpp @@ -2238,16 +2238,11 @@ bool event_handler::handle_event_command(const queued_event& event_info, // We have found a unit that matches the filter if(u != units->end() && ! screen->fogged(u->first)) { - screen->highlight_hex(u->first); screen->scroll_to_tile(u->first); - - u->second.set_extra_anim(*screen,u->first,cfg["flag"]); - while(!u->second.get_animation()->animation_would_finish()) { - screen->invalidate(u->first); - screen->draw(); - events::pump(); - screen->delay(10); - } + unit_animator animator; + animator.add_animation(&u->second,cfg["flag"],u->first); + animator.start_animations(); + animator.wait_for_end(); u->second.set_standing(*screen,u->first); screen->invalidate(u->first); screen->draw(); diff --git a/src/unit.cpp b/src/unit.cpp index 5f49f7e7df0..0ba550d7f0a 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -1520,131 +1520,42 @@ const surface unit::still_image(bool scaled) const void unit::set_standing(const game_display &disp,const gamemap::location& loc, bool with_bars) { - state_ = STATE_STANDING; - start_animation(disp,loc,choose_animation(disp,loc,"standing"),with_bars,true); -} -void unit::set_defending(const game_display &disp,const gamemap::location& loc, int damage,const attack_type* attack,const attack_type* secondary_attack,int swing_num) -{ - state_ = STATE_DEFENDING; - - unit_animation::hit_type hit_type; - if(damage >= hitpoints()) { - hit_type = unit_animation::KILL; - } else if(damage > 0) { - hit_type = unit_animation::HIT; - }else { - hit_type = unit_animation::MISS; - } - start_animation(disp,loc,choose_animation(disp,loc,"defend",damage,hit_type,attack,secondary_attack,swing_num),true); + start_animation(disp,loc,choose_animation(disp,loc,"standing"),with_bars,true,"",0,STATE_STANDING); } -void unit::set_extra_anim(const game_display &disp,const gamemap::location& loc, std::string flag) -{ - state_ = STATE_EXTRA; - start_animation(disp,loc,choose_animation(disp,loc,flag),false); - -} - -void unit::set_attacking(const game_display &disp,const gamemap::location& loc,int damage,const attack_type& type,const attack_type* secondary_attack,int swing_num) -{ - state_ = STATE_ATTACKING; - unit_animation::hit_type hit_type; - if(damage >= hitpoints()) { - hit_type = unit_animation::KILL; - } else if(damage > 0) { - hit_type = unit_animation::HIT; - }else { - hit_type = unit_animation::MISS; - } - start_animation(disp,loc,choose_animation(disp,loc,"attack",damage,hit_type,&type,secondary_attack,swing_num),true); - -} -void unit::set_leading(const game_display &disp,const gamemap::location& loc) -{ - state_ = STATE_LEADING; - start_animation(disp,loc,choose_animation(disp,loc,"leading"),true); -} -void unit::set_leveling_in(const game_display &disp,const gamemap::location& loc) -{ - state_ = STATE_LEVELIN; - start_animation(disp,loc,choose_animation(disp,loc,"levelin"),false); -} -void unit::set_leveling_out(const game_display &disp,const gamemap::location& loc) -{ - state_ = STATE_LEVELOUT; - start_animation(disp,loc,choose_animation(disp,loc,"levelout"),false); -} -void unit::set_recruited(const game_display &disp,const gamemap::location& loc) -{ - state_ = STATE_RECRUITED; - start_animation(disp,loc,choose_animation(disp,loc,"recruited"),false); -} -void unit::set_healed(const game_display &disp,const gamemap::location& loc, int healing) -{ - state_ = STATE_HEALED; - start_animation(disp,loc,choose_animation(disp,loc,"healed",healing),true); -} -void unit::set_poisoned(const game_display &disp,const gamemap::location& loc, int damage) -{ - state_ = STATE_POISONED; - start_animation(disp,loc,choose_animation(disp,loc,"poisoned",damage),true); -} - -void unit::set_teleporting(const game_display &disp,const gamemap::location& loc) -{ - state_ = STATE_TELEPORT; - start_animation(disp,loc,choose_animation(disp,loc,"teleport"),false); -} - -void unit::set_dying(const game_display &disp,const gamemap::location& loc,const attack_type* attack,const attack_type* secondary_attack) -{ - state_ = STATE_DYING; - start_animation(disp,loc,choose_animation(disp,loc,"death",0,unit_animation::KILL,attack,secondary_attack),false); -} -void unit::set_healing(const game_display &disp,const gamemap::location& loc,int healing) -{ - state_ = STATE_HEALING; - start_animation(disp,loc,choose_animation(disp,loc,"healing",healing),true); -} -void unit::set_victorious(const game_display &disp,const gamemap::location& loc,const attack_type* attack,const attack_type* secondary_attack) -{ - state_ = STATE_VICTORIOUS; - start_animation(disp,loc,choose_animation(disp,loc,"victory",0,unit_animation::KILL,attack,secondary_attack),true); -} void unit::set_walking(const game_display &disp,const gamemap::location& loc) { - if(state_ == STATE_WALKING && anim_ != NULL && anim_->matches(disp,loc,this,"movement") >unit_animation::MATCH_FAIL) { + if(state_ == STATE_ANIM && anim_ != NULL && anim_->matches(disp,loc,this,"movement") >unit_animation::MATCH_FAIL) { return; // finish current animation, don't start a new one + // is this the right behaviour ? we might not want that anymore } - state_ = STATE_WALKING; start_animation(disp,loc,choose_animation(disp,loc,"movement"),false); } void unit::set_idling(const game_display &disp,const gamemap::location& loc) { - state_ = STATE_IDLING; - start_animation(disp,loc,choose_animation(disp,loc,"idling"),true); + start_animation(disp,loc,choose_animation(disp,loc,"idling"),true,true,"",0,STATE_FORGET); } void unit::set_selecting(const game_display &disp,const gamemap::location& loc) { - state_ = STATE_SELECTING; - start_animation(disp,loc,choose_animation(disp,loc,"selected"),true); + start_animation(disp,loc,choose_animation(disp,loc,"selected"),true,true,"",0,STATE_FORGET); } -void unit::start_animation(const game_display &disp, const gamemap::location &loc,const unit_animation * animation,bool with_bars,bool cycles) +void unit::start_animation(const game_display &disp, const gamemap::location &loc,const unit_animation * animation,bool with_bars,bool cycles,const std::string text, const Uint32 text_color,STATE state) { if(!animation) { set_standing(disp,loc,with_bars); return ; } + state_ =state; draw_bars_ = with_bars; offset_=0; if(anim_) delete anim_; anim_ = new unit_animation(*animation); - anim_->start_animation(anim_->get_begin_time(),loc, loc.get_direction(facing_), cycles, disp.turbo_speed()); + anim_->start_animation(anim_->get_begin_time(),loc, loc.get_direction(facing_), cycles,text,text_color, disp.turbo_speed()); frame_begin_time_ = anim_->get_begin_time() -1; if (disp.idle_anim()) { next_idling_ = get_current_animation_tick() @@ -1656,7 +1567,7 @@ void unit::start_animation(const game_display &disp, const gamemap::location &lo void unit::restart_animation(const game_display& disp,int start_time, bool cycles) { if(!anim_) return; - anim_->start_animation(start_time,gamemap::location::null_location, gamemap::location::null_location, cycles, disp.turbo_speed()); + anim_->start_animation(start_time,gamemap::location::null_location, gamemap::location::null_location, cycles, "",0,disp.turbo_speed()); frame_begin_time_ = start_time -1; } @@ -1711,6 +1622,12 @@ void unit::redraw_unit(game_display& disp, const gamemap::location& loc) if(!anim_->sound().empty()) { sound::play_sound(anim_->sound()); } + if(!anim_->text().first.empty() ) { + game_display::get_singleton()->float_label(loc,anim_->text().first, + (anim_->text().second & 0x00FF0000) >> 16, + (anim_->text().second & 0x0000FF00) >> 8, + (anim_->text().second & 0x000000FF) >> 0); + } } double tmp_offset = anim_->offset(offset_); diff --git a/src/unit.hpp b/src/unit.hpp index c0f1e4a3aa4..956c414417b 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -124,7 +124,7 @@ public: void new_level(); //! Called on every draw void refresh(const game_display& disp,const gamemap::location& loc) { - if ((state_ == STATE_IDLING || state_ == STATE_SELECTING) && anim_ && anim_->animation_would_finish()) { + if (state_ == STATE_FORGET && anim_ && anim_->animation_would_finish()) { set_standing(disp, loc); return; } @@ -175,20 +175,7 @@ public: void set_standing(const game_display& disp,const gamemap::location& loc, bool with_bars = true); - void set_defending(const game_display &disp,const gamemap::location& loc, int damage,const attack_type* attack,const attack_type* secondary_attack,int swing_num); - void set_leading(const game_display& disp,const gamemap::location& loc); - void set_healing(const game_display& disp,const gamemap::location& loc,int damage); - void set_leveling_in(const game_display& disp,const gamemap::location& loc); - void set_leveling_out(const game_display& disp,const gamemap::location& loc); - void set_teleporting (const game_display& disp,const gamemap::location& loc); - void set_extra_anim(const game_display& disp,const gamemap::location& loc, std::string flag); - void set_dying(const game_display &disp,const gamemap::location& loc,const attack_type* attack,const attack_type* secondary_attack); void set_walking(const game_display& disp,const gamemap::location& loc); - void set_attacking( const game_display &disp,const gamemap::location& loc,int damage,const attack_type& type,const attack_type* secondary_attack,int swing_num); - void set_recruited(const game_display& disp,const gamemap::location& loc); - void set_healed(const game_display& disp,const gamemap::location& loc,int healing); - void set_victorious(const game_display &disp,const gamemap::location& loc,const attack_type* attack,const attack_type* secondary_attack); - void set_poisoned(const game_display& disp,const gamemap::location& loc,int damage); void set_idling(const game_display& disp,const gamemap::location& loc); void set_selecting(const game_display& disp,const gamemap::location& loc); void restart_animation(const game_display& disp,int start_time, bool cycles = false); @@ -244,13 +231,8 @@ public: void set_interrupted_move(const gamemap::location& interrupted_move) { interrupted_move_ = interrupted_move; } //! States for animation. - enum STATE { STATE_STANDING, STATE_ATTACKING, STATE_DEFENDING, - STATE_LEADING, STATE_HEALING, STATE_WALKING, - STATE_LEVELIN, STATE_LEVELOUT, - STATE_DYING, STATE_EXTRA, STATE_TELEPORT, - STATE_RECRUITED, STATE_HEALED, STATE_POISONED, - STATE_IDLING, STATE_SELECTING, STATE_VICTORIOUS}; - void start_animation(const game_display &disp, const gamemap::location &loc,const unit_animation* animation, bool with_bars,bool cycles=false); + enum STATE { STATE_STANDING, STATE_FORGET, STATE_ANIM}; + void start_animation(const game_display &disp, const gamemap::location &loc,const unit_animation* animation, bool with_bars,bool cycles=false,const std::string text = "", const Uint32 text_color =0,STATE state = STATE_ANIM); //! The name of the file to game_display (used in menus). const std::string& absolute_image() const { return cfg_["image"]; } diff --git a/src/unit_animation.cpp b/src/unit_animation.cpp index 796706d8357..d5e15bb4bf1 100644 --- a/src/unit_animation.cpp +++ b/src/unit_animation.cpp @@ -369,21 +369,25 @@ void unit_animation::initialize_anims( std::vector & animations, (**anim_itor)["apply_to"] ="healed"; (**anim_itor)["value"]=(**anim_itor)["healing"]; animations.push_back(unit_animation(**anim_itor)); + animations.back().sub_anims_["_healed_sound"] = crude_animation(); + animations.back().sub_anims_["_healed_sound"].add_frame(1,unit_frame(image::locator(),1,"","",0,"","","","","","heal.wav"),true); //lg::wml_error<<"healed animations are deprecate, support will be removed in 1.3.11 (in unit "< & animations, expanded_cfg = unit_animation::prepare_animation(cfg,"teleport_anim"); const config::child_list& teleports = expanded_cfg.get_children("teleport_anim"); for(anim_itor = teleports.begin(); anim_itor != teleports.end(); ++anim_itor) { - (**anim_itor)["apply_to"] ="teleport"; + (**anim_itor)["apply_to"] ="pre_teleport"; animations.push_back(unit_animation(**anim_itor)); + animations.back().unit_anim_.remove_frames_after(0); + (**anim_itor)["apply_to"] ="post_teleport"; + animations.push_back(unit_animation(**anim_itor)); + animations.back().unit_anim_.remove_frames_until(0); //lg::wml_error<<"teleport animations are deprecate, support will be removed in 1.3.11 (in unit "< unit_animation::crude_animation::text() const +{ + return get_current_frame().text(); +} + bool unit_animation::crude_animation::need_update() const { if(animated::need_update()) return true; @@ -568,7 +582,6 @@ unit_animation::crude_animation::crude_animation( blend_ratio_ = progressive_double(cfg[frame_string+"blend_ratio"],get_animation_duration()); highlight_ratio_ = progressive_double(cfg[frame_string+"alpha"],get_animation_duration()); offset_ = progressive_double(cfg[frame_string+"offset"],get_animation_duration()); - if(!halo_.does_not_change() || !halo_x_.does_not_change() || !halo_y_.does_not_change() || @@ -638,9 +651,14 @@ int unit_animation::get_begin_time() const return result; } -void unit_animation::start_animation(int start_time,const gamemap::location &src, const gamemap::location &dst, bool cycles, double acceleration) +void unit_animation::start_animation(int start_time,const gamemap::location &src, const gamemap::location &dst, bool cycles, const std::string text, const Uint32 text_color, double acceleration) { - unit_anim_.start_animation(start_time, src, dst, cycles, acceleration); + unit_anim_.start_animation(start_time, src, dst, cycles,acceleration); + if(!text.empty()) { + crude_animation crude_build; + crude_build.add_frame(1,unit_frame(image::locator(),1,"","",0,"","","","","","",text,text_color),true); + sub_anims_["_add_text"] = crude_build; + } std::map::iterator anim_itor =sub_anims_.begin(); for( /*null*/; anim_itor != sub_anims_.end() ; anim_itor++) { anim_itor->second.start_animation(start_time,src,dst,cycles,acceleration); @@ -648,6 +666,7 @@ void unit_animation::start_animation(int start_time,const gamemap::location &src } void unit_animation::redraw() { + std::map::iterator anim_itor =sub_anims_.begin(); for( /*null*/; anim_itor != sub_anims_.end() ; anim_itor++) { anim_itor->second.redraw(); @@ -666,8 +685,17 @@ void unit_animation::crude_animation::redraw() update_last_draw_time(); const unit_frame& current_frame= get_current_frame(); - if(!current_frame.sound().empty() && get_current_frame_begin_time() != last_frame_begin_time_ ) { + if(get_current_frame_begin_time() != last_frame_begin_time_ ) { + // stuff sthat should be done only once per frame + if(!current_frame.sound().empty() ) { sound::play_sound(current_frame.sound()); + } + if(!current_frame.text().first.empty() ) { + game_display::get_singleton()->float_label(src_,current_frame.text().first, + (current_frame.text().second & 0x00FF0000) >> 16, + (current_frame.text().second & 0x0000FF00) >> 8, + (current_frame.text().second & 0x000000FF) >> 0); + } } last_frame_begin_time_ = get_current_frame_begin_time(); image::locator image_loc; @@ -752,4 +780,87 @@ void unit_animation::crude_animation::start_animation(int start_time, src_ = src; dst_ = dst; } +}; + +void unit_animator::add_animation(unit* animated_unit,const std::string& event, + const gamemap::location &src , const int value,bool with_bars,bool cycles, + const std::string text,const Uint32 text_color, + const unit_animation::hit_type hit_type, + const attack_type* attack, const attack_type* second_attack, int swing_num) +{ + if(!animated_unit) return; + anim_elem tmp; + game_display*disp = game_display::get_singleton(); + tmp.my_unit = animated_unit; + tmp.text = text; + tmp.text_color = text_color; + tmp.src = src; + tmp.with_bars= with_bars; + tmp.cycles = cycles; + tmp.animation = animated_unit->choose_animation(*disp,src,event,value,hit_type,attack,second_attack,swing_num); +if(!tmp.animation) return; + + + + start_time_ = maximum(start_time_,tmp.animation->get_begin_time()); + animated_units_.push_back(tmp); } +void unit_animator::replace_anim_if_invalid(unit* animated_unit,const std::string& event, + const gamemap::location &src , const int value,bool with_bars,bool cycles, + const std::string text,const Uint32 text_color, + const unit_animation::hit_type hit_type, + const attack_type* attack, const attack_type* second_attack, int swing_num) +{ + if(!animated_unit) return; + game_display*disp = game_display::get_singleton(); + if(animated_unit->get_animation() && + !animated_unit->get_animation()->animation_would_finish() && + animated_unit->get_animation()->matches(*disp,src,animated_unit,event,value,hit_type,attack,second_attack,swing_num) >unit_animation::MATCH_FAIL) { + anim_elem tmp; + tmp.my_unit = animated_unit; + tmp.text = text; + tmp.text_color = text_color; + tmp.src = src; + tmp.with_bars= with_bars; + tmp.cycles = cycles; + tmp.animation = NULL; + animated_units_.push_back(tmp); + }else { + add_animation(animated_unit,event,src,value,with_bars,cycles,text,text_color,hit_type,attack,second_attack,swing_num); + } +} +void unit_animator::start_animations() +{ + game_display*disp = game_display::get_singleton(); + for(std::vector::iterator anim = animated_units_.begin(); anim != animated_units_.end();anim++) { + if(anim->animation) { + anim->my_unit->start_animation(*disp,anim->src, anim->animation,anim->with_bars, anim->cycles,anim->text,anim->text_color); + anim->animation = NULL; + } + + } +} + +bool unit_animator::would_end() const +{ + bool finished = true; + for(std::vector::const_iterator anim = animated_units_.begin(); anim != animated_units_.end();anim++) { + finished &= anim->my_unit->get_animation()->animation_would_finish(); + } + return finished; +} +void unit_animator::wait_for_end() const +{ + bool finished = false; + game_display*disp = game_display::get_singleton(); + while(!finished) { + disp->draw(); + events::pump(); + disp->delay(10); + finished = true; + for(std::vector::const_iterator anim = animated_units_.begin(); anim != animated_units_.end();anim++) { + finished &= anim->my_unit->get_animation()->animation_would_finish(); + } + } +} + diff --git a/src/unit_animation.hpp b/src/unit_animation.hpp index dde72d07bc6..d021b3bdecc 100644 --- a/src/unit_animation.hpp +++ b/src/unit_animation.hpp @@ -51,7 +51,7 @@ class unit_animation int get_begin_time() const; int get_end_time() const; int get_animation_time() const{ return unit_anim_.get_animation_time() ; }; - void start_animation(int start_time,const gamemap::location &src = gamemap::location::null_location, const gamemap::location &dst = gamemap::location::null_location , bool cycles=false, double acceleration=1); + void start_animation(int start_time,const gamemap::location &src = gamemap::location::null_location, const gamemap::location &dst = gamemap::location::null_location , bool cycles=false, const std::string text="", const Uint32 text_color=0, double acceleration=1); const int get_current_frame_begin_time() const{ return unit_anim_.get_current_frame_begin_time() ; }; void redraw(); @@ -68,10 +68,11 @@ class unit_animation double blend_ratio(const double default_val = 0) const{ return unit_anim_.blend_ratio(default_val); }; fixed_t highlight_ratio(const float default_val = 1.0) const{ return unit_anim_.highlight_ratio(default_val); }; double offset(double default_val =0.0) const{ return unit_anim_.offset(default_val); }; + std::pair text() const { return unit_anim_.text() ; }; private: static config prepare_animation(const config &cfg,const std::string animation_tag); explicit unit_animation(const config& cfg,const std::string frame_string =""); - explicit unit_animation(int start_time,const unit_frame &frame,const std::string& even="",const int variation=0); + explicit unit_animation(int start_time,const unit_frame &frame,const std::string& event="",const int variation=DEFAULT_ANIM); class crude_animation:public animated { public: @@ -98,6 +99,7 @@ class unit_animation double blend_ratio(const double default_val = 0) const; fixed_t highlight_ratio(const float default_val = 1.0) const; double offset(double default_val =0.0) const; + std::pair text() const ; void redraw( ); void start_animation(int start_time,const gamemap::location& src,const gamemap::location& dst, bool cycles=false, double acceleration=1); private: @@ -132,5 +134,41 @@ class unit_animation crude_animation unit_anim_; }; +class unit_animator +{ + public: + unit_animator():start_time_(INT_MIN){}; + void add_animation(unit* animated_unit,const std::string& event, + const gamemap::location &src = gamemap::location::null_location, + const int value=0,bool with_bars = false,bool cycles = false, + const std::string text="",const Uint32 text_color=0, + const unit_animation::hit_type hit_type = unit_animation::INVALID, + const attack_type* attack=NULL, const attack_type* second_attack = NULL, + int swing_num =0); + void replace_anim_if_invalid(unit* animated_unit,const std::string& event, + const gamemap::location &src = gamemap::location::null_location, + const int value=0,bool with_bars = false,bool cycles = false, + const std::string text="",const Uint32 text_color=0, + const unit_animation::hit_type hit_type = unit_animation::INVALID, + const attack_type* attack=NULL, const attack_type* second_attack = NULL, + int swing_num =0); + void start_animations(); + void empty(){start_time_ = INT_MIN ; animated_units_.clear();}; + + bool would_end() const; + void wait_for_end() const; + private: + typedef struct { + unit *my_unit; + const unit_animation * animation; + std::string text; + Uint32 text_color; + gamemap::location src; + bool with_bars; + bool cycles; + } anim_elem; + std::vector animated_units_; + int start_time_; +}; #endif diff --git a/src/unit_display.cpp b/src/unit_display.cpp index 24d6a5c7d6b..82ff04389c7 100644 --- a/src/unit_display.cpp +++ b/src/unit_display.cpp @@ -42,28 +42,22 @@ static void teleport_unit_between( const gamemap::location& a, const gamemap::lo if(!disp || disp->video().update_locked() || disp->fogged(a) && disp->fogged(b)) { return; } + disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true); - temp_unit.set_teleporting(*disp,a); if (!disp->fogged(a)) { // teleport - disp->scroll_to_tile(a,game_display::ONSCREEN); - while(!temp_unit.get_animation()->animation_finished() && temp_unit.get_animation()->get_animation_time() < 0) { - disp->invalidate(a); - disp->place_temporary_unit(temp_unit, a); - disp->draw(); - events::pump(); - disp->delay(10); - } + disp->place_temporary_unit(temp_unit,a); + unit_animator animator; + animator.add_animation(&temp_unit,"pre_teleport",a); + animator.start_animations(); + animator.wait_for_end(); } if (!disp->fogged(b)) { // teleport - temp_unit.restart_animation(*disp,0); - disp->scroll_to_tile(b,game_display::ONSCREEN); - while(!temp_unit.get_animation()->animation_finished()) { - disp->invalidate(b); - disp->place_temporary_unit(temp_unit, b); - disp->draw(); - events::pump(); - disp->delay(10); - } + disp->place_temporary_unit(temp_unit,b); + disp->scroll_to_tiles(b,a,game_display::ONSCREEN,true); + unit_animator animator; + animator.add_animation(&temp_unit,"post_teleport",b); + animator.start_animations(); + animator.wait_for_end(); } temp_unit.set_standing(*disp,b); disp->update_display(); @@ -72,6 +66,9 @@ static void teleport_unit_between( const gamemap::location& a, const gamemap::lo static void move_unit_between( const gamemap& map, const gamemap::location& a, const gamemap::location& b, unit& temp_unit) { +//NOTES TO SELF (boucman) +// get rid of speed dependant gliding +// add some sort of auto slide anyway game_display* disp = game_display::get_singleton(); if(!disp || disp->video().update_locked() || disp->fogged(a) && disp->fogged(b)) { return; @@ -91,13 +88,15 @@ static void move_unit_between( const gamemap& map, const gamemap::location& a, c int mvt_time = 1; while(mvt_time < total_mvt_time-1) { // One draw in each hex at least + unit_animator animator; disp->delay(10); mvt_time = SDL_GetTicks() -start_time; if(mvt_time >=total_mvt_time) mvt_time = total_mvt_time -1; double pos =double(mvt_time)/total_mvt_time; const gamemap::location& ref_loc =pos<0.5?a:b; if(pos >= 0.5) pos = pos -1; - temp_unit.set_walking(*disp,ref_loc); + animator.replace_anim_if_invalid(&temp_unit,"movement",ref_loc); + animator.start_animations(); temp_unit.set_offset(pos); disp->place_temporary_unit(temp_unit,ref_loc); disp->draw(); @@ -181,32 +180,13 @@ void unit_die(const gamemap::location& loc, unit& loser, if(!disp ||disp->video().update_locked() || disp->fogged(loc) || preferences::show_combat() == false) { return; } + unit_animator animator; + animator.add_animation(&loser,"death",loc,0,false,false,"",0,unit_animation::KILL,attack,secondary_attack,0); + animator.add_animation(winner,"victory",loc.get_direction(loser.facing()),0,false,false,"",0, + unit_animation::KILL,secondary_attack,attack,0); + animator.start_animations(); + animator.wait_for_end(); - loser.set_dying(*disp,loc,attack,secondary_attack); - if(winner == NULL) { // Test to see if there is no victor. - - while(!loser.get_animation()->animation_finished()) { - - disp->invalidate(loc); - disp->draw(); - events::pump(); - disp->delay(10); - } - return; - } - winner->set_victorious(*disp,loc,attack,secondary_attack); - int start_time = minimum(loser.get_animation()->get_begin_time(),winner->get_animation()->get_begin_time()); - - winner->restart_animation(*disp,start_time); - loser.restart_animation(*disp,start_time); - - while((!loser.get_animation()->animation_would_finish()) || ((!winner->get_animation()->animation_would_finish()))) { - - disp->invalidate(loc); - disp->draw(); - events::pump(); - disp->delay(10); - } } @@ -234,91 +214,53 @@ void unit_attack( const unit_map::iterator def = units.find(b); assert(def != units.end()); + unit& defender = def->second; att->second.set_facing(a.get_relative_dir(b)); def->second.set_facing(b.get_relative_dir(a)); - int start_time = 500; - int end_time = 0; - - bool def_was_hidden = def->second.get_hidden(); - def->second.set_hidden(true); - unit defender = def->second; - disp->place_temporary_unit(defender,b); - defender.set_hidden(false); - - - attacker.set_attacking(*disp,a,damage,attack,secondary_attack,swing); - start_time=minimum(start_time,attacker.get_animation()->get_begin_time()); - end_time=attacker.get_animation()->get_end_time(); - - defender.set_defending(*disp,b,damage,&attack, secondary_attack, swing); - start_time=minimum(start_time,defender.get_animation()->get_begin_time()); - + unit_animator animator; const gamemap::location leader_loc = under_leadership(units,a); unit_map::iterator leader = units.end(); - if(leader_loc.valid()){ - LOG_DP << "found leader at " << leader_loc << '\n'; - leader = units.find(leader_loc); - assert(leader != units.end()); - leader->second.set_facing(leader_loc.get_relative_dir(a)); - leader->second.set_leading(*disp,leader_loc); - start_time=minimum(start_time,leader->second.get_animation()->get_begin_time()); + + { + std::string text ; + if(damage) text = lexical_cast(damage); + if(!hit_text.empty()) { + text.insert(text.begin(),hit_text.size()/2,' '); + text = text + "\n" + hit_text; + } + + unit_animation::hit_type hit_type; + if(damage >= defender.hitpoints()) { + hit_type = unit_animation::KILL; + } else if(damage > 0) { + hit_type = unit_animation::HIT; + }else { + hit_type = unit_animation::MISS; + } + animator.add_animation(&attacker,"attack",att->first,damage,true,false,"",0,hit_type,&attack,secondary_attack,swing); + animator.add_animation(&defender,"defend",def->first,damage,true,false,text,display::rgb(255,0,0),hit_type,&attack,secondary_attack,swing); + + if(leader_loc.valid()){ + leader = units.find(leader_loc); + leader->second.set_facing(leader_loc.get_relative_dir(a)); + assert(leader != units.end()); + animator.add_animation(&leader->second,"leading",leader_loc,damage,true,false,"",0,hit_type,&attack,secondary_attack,swing); + } } - gamemap::location update_tiles[6]; - get_adjacent_tiles(b,update_tiles); - attacker.restart_animation(*disp,start_time); - defender.restart_animation(*disp,start_time); - if(leader_loc.valid()) leader->second.restart_animation(*disp,start_time); - int animation_time = start_time; - bool sound_played = false; - while(!hide && ( - !attacker.get_animation()->animation_would_finish() || - !defender.get_animation()->animation_would_finish() || - (leader_loc.valid() && !leader->second.get_animation()->animation_would_finish() ) || - damage > 0) - ){ - - if(!sound_played && animation_time > 0) { - sound_played = true; - std::string text ; - if(damage) text = lexical_cast(damage); - if(!hit_text.empty()) { - text.insert(text.begin(),hit_text.size()/2,' '); - text = text + "\n" + hit_text; - } - sound::play_sound(defender.get_hit_sound()); - disp->float_label(b,text,255,0,0); - disp->invalidate_unit(); - } - if(damage > 0 && animation_time > 0) { - defender.take_hit(1); - damage--; - disp->invalidate_unit(); - } - disp->invalidate(b); - disp->invalidate(a); - if(leader_loc.valid()) disp->invalidate(leader_loc); - disp->draw(); - events::pump(); - if(attacker.get_animation()->animation_finished()) { - attacker.set_offset(0.0); - } - disp->delay(10); - animation_time = attacker.get_animation()->get_animation_time(); - } + animator.start_animations(); + animator.wait_for_end(); if(leader_loc.valid()) leader->second.set_standing(*disp,leader_loc); att->second.set_standing(*disp,a); def->second.set_standing(*disp,b); - def->second.set_hidden(def_was_hidden); - disp->remove_temporary_unit(); } @@ -332,89 +274,42 @@ void unit_recruited(gamemap::location& loc) u->second.set_hidden(true); disp->scroll_to_tile(loc,game_display::ONSCREEN); disp->draw(); - u->second.set_recruited(*disp,loc); u->second.set_hidden(false); - while(!u->second.get_animation()->animation_finished()) { - - disp->invalidate(loc); - disp->draw(); - events::pump(); - disp->delay(10); - } + unit_animator animator; + animator.add_animation(&u->second,"recruited",loc); + animator.start_animations(); + animator.wait_for_end(); u->second.set_standing(*disp,loc); if (loc==disp->mouseover_hex()) disp->invalidate_unit(); } -void unit_healing(unit& healed_p,gamemap::location& healed_loc, std::vector healers, int healing) +void unit_healing(unit& healed,gamemap::location& healed_loc, std::vector healers, int healing) { game_display* disp = game_display::get_singleton(); if(!disp || disp->video().update_locked() || disp->fogged(healed_loc)) return; if(healing==0) return; // This is all the pretty stuff. - int start_time = INT_MAX; disp->scroll_to_tile(healed_loc, game_display::ONSCREEN); disp->select_hex(healed_loc); - unit healed = healed_p; - bool was_hidden = healed.get_hidden(); - healed_p.set_hidden(true); - disp->place_temporary_unit(healed,healed_loc); - healed.set_hidden(false); + unit_animator animator; for(std::vector::iterator heal_anim_it = healers.begin(); heal_anim_it != healers.end(); ++heal_anim_it) { (*heal_anim_it)->second.set_facing((*heal_anim_it)->first.get_relative_dir(healed_loc)); - (*heal_anim_it)->second.set_healing(*disp,(*heal_anim_it)->first,healing); - start_time = minimum((*heal_anim_it)->second.get_animation()->get_begin_time(),start_time); + animator.add_animation(&(*heal_anim_it)->second,"healing",(*heal_anim_it)->first,healing); } if (healing < 0) { - healed.set_poisoned(*disp,healed_loc, -healing); - start_time = minimum(start_time, healed.get_animation()->get_begin_time()); - //! @todo FIXME - sound::play_sound("poison.ogg"); - disp->float_label(healed_loc, lexical_cast(-healing), 255,0,0); + animator.add_animation(&healed,"poisoned",healed_loc,-healing,false,false,lexical_cast(-healing), display::rgb(255,0,0)); } else { - healed.set_healed(*disp,healed_loc, healing); - start_time = minimum(start_time, healed.get_animation()->get_begin_time()); - sound::play_sound("heal.wav"); - disp->float_label(healed_loc, lexical_cast(healing), 0,255,0); - } - disp->draw(); - events::pump(); - // Restart all anims in a synchronized way - healed.restart_animation(*disp, start_time); - for(std::vector::iterator heal_reanim_it = healers.begin(); heal_reanim_it != healers.end(); ++heal_reanim_it) { - (*heal_reanim_it)->second.restart_animation(*disp, start_time); + animator.add_animation(&healed,"healed",healed_loc,healing,false,false,lexical_cast(healing), display::rgb(0,255,0)); } + animator.start_animations(); + animator.wait_for_end(); - bool finished; - do { - finished = (healed.get_animation()->animation_finished() && healing==0); - disp->invalidate(healed_loc); - for(std::vector::iterator heal_fanim_it = healers.begin(); heal_fanim_it != healers.end(); ++heal_fanim_it) { - finished &= (*heal_fanim_it)->second.get_animation()->animation_finished(); - disp->invalidate((*heal_fanim_it)->first); - } - //! @todo TODO : Adapt HP change speed to turbo_speed - if(healing > 0) { - healed.heal(1); - healing--; - } else if (healing < 0) { - healed.take_hit(1); - healing++; - } - disp->draw(); - events::pump(); - disp->delay(10); - } while (!finished); - - healed_p.set_standing(*disp,healed_loc); - healed_p.set_hidden(was_hidden); - disp->remove_temporary_unit(); + healed.set_standing(*disp,healed_loc); for(std::vector::iterator heal_sanim_it = healers.begin(); heal_sanim_it != healers.end(); ++heal_sanim_it) { (*heal_sanim_it)->second.set_standing(*disp,(*heal_sanim_it)->first); } - disp->update_display(); - events::pump(); } } // end unit_display namespace diff --git a/src/unit_frame.cpp b/src/unit_frame.cpp index 4943777873d..2ec45b1b667 100644 --- a/src/unit_frame.cpp +++ b/src/unit_frame.cpp @@ -151,6 +151,7 @@ template class progressive_; unit_frame::unit_frame() : image_(), image_diagonal_(),halo_(), sound_(), + text_(""),text_color_(0), halo_x_(), halo_y_(), duration_(0), blend_with_(0),blend_ratio_(), highlight_ratio_(""), offset_() @@ -161,10 +162,12 @@ unit_frame::unit_frame(const image::locator& image, int duration, const std::string& highlight, const std::string& offset, Uint32 blend_color, const std::string& blend_rate, const std::string& in_halo, const std::string& halox, const std::string& haloy, - const image::locator & diag,const std::string & sound) : + const image::locator & diag,const std::string & sound, + const std::string & text, const Uint32 text_color) : image_(image),image_diagonal_(diag), halo_(in_halo,duration), sound_(sound), + text_(text), text_color_(text_color), halo_x_(halox,duration), halo_y_(haloy,duration), duration_(duration), @@ -183,6 +186,9 @@ unit_frame::unit_frame(const config& cfg) image_ = image::locator(cfg["image"]); image_diagonal_ = image::locator(cfg["image_diagonal"]); sound_ = cfg["sound"]; + text_ = cfg["text"]; + std::vector tmp_string_vect=utils::split(cfg["text_color"]); + if(tmp_string_vect.size() ==3) text_color_ = display::rgb(atoi(tmp_string_vect[0].c_str()),atoi(tmp_string_vect[1].c_str()),atoi(tmp_string_vect[2].c_str())); if(!cfg["duration"].empty()) { duration_ = atoi(cfg["duration"].c_str()); } else { @@ -191,8 +197,8 @@ unit_frame::unit_frame(const config& cfg) halo_ = progressive_string(cfg["halo"],duration_); halo_x_ = progressive_int(cfg["halo_x"],duration_); halo_y_ = progressive_int(cfg["halo_y"],duration_); - std::vector tmp_blend=utils::split(cfg["blend_color"]); - if(tmp_blend.size() ==3) blend_with_= display::rgb(atoi(tmp_blend[0].c_str()),atoi(tmp_blend[1].c_str()),atoi(tmp_blend[2].c_str())); + tmp_string_vect=utils::split(cfg["blend_color"]); + if(tmp_string_vect.size() ==3) blend_with_= display::rgb(atoi(tmp_string_vect[0].c_str()),atoi(tmp_string_vect[1].c_str()),atoi(tmp_string_vect[2].c_str())); blend_ratio_ = progressive_double(cfg["blend_ratio"],duration_); highlight_ratio_ = progressive_double(cfg["alpha"],duration_); offset_ = progressive_double(cfg["offset"],duration_); diff --git a/src/unit_frame.hpp b/src/unit_frame.hpp index f0fff89aba7..273d217a935 100644 --- a/src/unit_frame.hpp +++ b/src/unit_frame.hpp @@ -68,7 +68,7 @@ class unit_frame { Uint32 blend_color = 0, const std::string& blend_rate = "", const std::string & in_halo = "", const std::string & halox = "",const std::string & haloy = "", - const image::locator & diag ="",const std::string & sound = ""); + const image::locator & diag ="",const std::string & sound = "",const std::string & text = "", const Uint32 text_color=0); explicit unit_frame(const config& cfg); image::locator image() const { return image_ ;} image::locator image_diagonal() const { return image_diagonal_ ; } @@ -76,6 +76,7 @@ class unit_frame { { return halo_.get_current_element(current_time,default_val); } std::string sound() const { return sound_ ; }; + std::pair text() const { return std::pair(text_,text_color_) ; }; int halo_x(int current_time,const int default_val=0) const { return halo_x_.get_current_element(current_time,default_val); } int halo_y(int current_time,const int default_val=0) const { return halo_y_.get_current_element(current_time,default_val); } int duration() const { return duration_; } @@ -97,6 +98,8 @@ class unit_frame { progressive_string halo_; std::string sound_; + std::string text_; + Uint32 text_color_; progressive_int halo_x_; progressive_int halo_y_; int duration_;