first go at the new animation API, still a WIP.

No regression expected. if anything changes, please tell me.

Note that:

* Fire and forget doesn't work yet, don't try using it

* I will probably change how mvt anims work as discussed in IRC, but
  that's for a future commit
This commit is contained in:
Jérémy Rosen 2007-12-17 17:51:36 +00:00
parent 38bad0370e
commit 5aba45d829
12 changed files with 297 additions and 328 deletions

View file

@ -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

View file

@ -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

View file

@ -284,4 +284,26 @@ int animated<T,T_void_value>::get_end_time() const
return starting_frame_time_;
return frames_.back().start_time_ + frames_.back().duration_;
}
template<typename T, typename T_void_value>
void animated<T,T_void_value>::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<typename T, typename T_void_value>
void animated<T,T_void_value>::remove_frames_after(int new_ending_time)
{
int last_start_time = starting_frame_time_;
typename std::vector<frame>::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());
}

View file

@ -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();

View file

@ -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();

View file

@ -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_);

View file

@ -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"]; }

View file

@ -369,21 +369,25 @@ void unit_animation::initialize_anims( std::vector<unit_animation> & 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 "<<cfg["name"]<<")\n";
//lg::wml_error<<"please put it with an [animation] tag and apply_to=healed flag\n";
}
if(with_default) animations.push_back(unit_animation(0,unit_frame(image::locator(cfg["image"]),240,"1.0","",display::rgb(255,255,255),"0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30"),"healed",unit_animation::DEFAULT_ANIM));
if(with_default) animations.push_back(unit_animation(0,unit_frame(image::locator(cfg["image"]),240,"1.0","",display::rgb(255,255,255),"0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30","","","","","heal.wav"),"healed",unit_animation::DEFAULT_ANIM));
// Always have a healed animation
expanded_cfg = unit_animation::prepare_animation(cfg,"poison_anim");
const config::child_list& poison_anims = expanded_cfg.get_children("poison_anim");
for(anim_itor = poison_anims.begin(); anim_itor != poison_anims.end(); ++anim_itor) {
(**anim_itor)["apply_to"] ="poison";
(**anim_itor)["apply_to"] ="poisoned";
(**anim_itor)["value"]=(**anim_itor)["damage"];
animations.push_back(unit_animation(**anim_itor));
animations.back().sub_anims_["_poison_sound"] = crude_animation();
animations.back().sub_anims_["_poison_sound"].add_frame(1,unit_frame(image::locator(),1,"","",0,"","","","","","poison.ogg"),true);
//lg::wml_error<<"poison animations are deprecate, support will be removed in 1.3.11 (in unit "<<cfg["name"]<<")\n";
//lg::wml_error<<"please put it with an [animation] tag and apply_to=poison flag\n";
}
if(with_default) animations.push_back(unit_animation(0,unit_frame(image::locator(cfg["image"]),240,"1.0","",display::rgb(0,255,0),"0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30"),"poison",unit_animation::DEFAULT_ANIM));
if(with_default) animations.push_back(unit_animation(0,unit_frame(image::locator(cfg["image"]),240,"1.0","",display::rgb(0,255,0),"0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30","","","","","poison.ogg"),"poisoned",unit_animation::DEFAULT_ANIM));
// Always have a poison animation
expanded_cfg = unit_animation::prepare_animation(cfg,"movement_anim");
const config::child_list& movement_anims = expanded_cfg.get_children("movement_anim");
@ -479,12 +483,17 @@ void unit_animation::initialize_anims( std::vector<unit_animation> & 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 "<<cfg["name"]<<")\n";
//lg::wml_error<<"please put it with an [animation] tag and apply_to=teleport flag\n";
}
if(with_default) animations.push_back(unit_animation(-20,unit_frame(image::locator(cfg["image"]),40),"teleport",unit_animation::DEFAULT_ANIM));
if(with_default) animations.push_back(unit_animation(-150,unit_frame(image::locator(cfg["image"]),150,"1~0"),"pre_teleport",unit_animation::DEFAULT_ANIM));
if(with_default) animations.push_back(unit_animation(0,unit_frame(image::locator(cfg["image"]),150,"0~1"),"post_teleport",unit_animation::DEFAULT_ANIM));
// Always have a defensive animation
}
@ -519,6 +528,11 @@ double unit_animation::crude_animation::offset(double default_val) const
return get_current_frame().offset(get_current_frame_time(),offset_.get_current_element(get_animation_time() - get_begin_time(),default_val)) ;
}
std::pair<std::string,Uint32> unit_animation::crude_animation::text() const
{
return get_current_frame().text();
}
bool unit_animation::crude_animation::need_update() const
{
if(animated<unit_frame>::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<std::string,crude_animation>::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<std::string,crude_animation>::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<int>(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<anim_elem>::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<anim_elem>::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<anim_elem>::const_iterator anim = animated_units_.begin(); anim != animated_units_.end();anim++) {
finished &= anim->my_unit->get_animation()->animation_would_finish();
}
}
}

View file

@ -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<std::string,Uint32> 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<unit_frame>
{
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<std::string,Uint32> 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<anim_elem> animated_units_;
int start_time_;
};
#endif

View file

@ -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<int>(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<int>(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<int>(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<int>(start_time,leader->second.get_animation()->get_begin_time());
{
std::string text ;
if(damage) text = lexical_cast<std::string>(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<std::string>(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<unit_map::iterator> healers, int healing)
void unit_healing(unit& healed,gamemap::location& healed_loc, std::vector<unit_map::iterator> 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<unit_map::iterator>::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<int>((*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<int>(start_time, healed.get_animation()->get_begin_time());
//! @todo FIXME
sound::play_sound("poison.ogg");
disp->float_label(healed_loc, lexical_cast<std::string>(-healing), 255,0,0);
animator.add_animation(&healed,"poisoned",healed_loc,-healing,false,false,lexical_cast<std::string>(-healing), display::rgb(255,0,0));
} else {
healed.set_healed(*disp,healed_loc, healing);
start_time = minimum<int>(start_time, healed.get_animation()->get_begin_time());
sound::play_sound("heal.wav");
disp->float_label(healed_loc, lexical_cast<std::string>(healing), 0,255,0);
}
disp->draw();
events::pump();
// Restart all anims in a synchronized way
healed.restart_animation(*disp, start_time);
for(std::vector<unit_map::iterator>::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<std::string>(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<unit_map::iterator>::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<unit_map::iterator>::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

View file

@ -151,6 +151,7 @@ template class progressive_<double>;
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<std::string> 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<std::string> 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_);

View file

@ -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<std::string,Uint32> text() const { return std::pair<std::string,Uint32>(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_;