added support for defensive animations

This commit is contained in:
Dave White 2004-06-23 15:54:16 +00:00
parent 8e3006652a
commit 79a5f2a21e
5 changed files with 224 additions and 146 deletions

View File

@ -801,15 +801,24 @@ const std::string& unit::image() const
switch(state_) {
case STATE_NORMAL: return type_->image();
case STATE_DEFENDING_LONG:
return type_->image_defensive(attack_type::LONG_RANGE);
case STATE_DEFENDING_SHORT:
return type_->image_defensive(attack_type::SHORT_RANGE);
case STATE_DEFENDING_SHORT: {
const attack_type::RANGE range = (state_ == STATE_DEFENDING_LONG) ? attack_type::LONG_RANGE : attack_type::SHORT_RANGE;
const unit_animation* const anim = type_->defend_animation(getsHit_,range);
if(anim != NULL) {
const std::string* img = anim->get_frame(attackingMilliseconds_);
if(img != NULL) {
return *img;
}
}
return type_->image_defensive(range);
}
case STATE_ATTACKING: {
if(attackType_ == NULL)
return type_->image();
const std::string* const img =
attackType_->get_frame(attackingMilliseconds_);
attackType_->animation().get_frame(attackingMilliseconds_);
if(img == NULL)
return type_->image_fighting(attackType_->range());
@ -825,10 +834,12 @@ const std::string& unit::image() const
}
}
void unit::set_defending(bool newval, attack_type::RANGE range)
void unit::set_defending(bool newval, bool hits, int ms, attack_type::RANGE range)
{
state_ = newval ? (range == attack_type::LONG_RANGE ? STATE_DEFENDING_LONG :
STATE_DEFENDING_SHORT): STATE_NORMAL;
attackingMilliseconds_ = ms;
getsHit_ = hits;
}
void unit::set_attacking(bool newval, const attack_type* type, int ms)

View File

@ -112,7 +112,7 @@ public:
//(could be in the middle of an attack etc)
const std::string& image() const;
void set_defending(bool newval,
void set_defending(bool newval, bool hits=false, int ms=0,
attack_type::RANGE range=attack_type::LONG_RANGE);
void set_attacking(bool newval, const attack_type* type=NULL, int ms=0);
@ -150,6 +150,7 @@ private:
STATE state_;
const attack_type* attackType_;
int attackingMilliseconds_;
bool getsHit_;
int hitpoints_;
int maxHitpoints_, backupMaxHitpoints_;

View File

@ -203,8 +203,6 @@ bool unit_attack_ranged(display& disp, unit_map& units, const gamemap& map,
const unit_map::iterator att = units.find(a);
const unit_map::iterator def = units.find(b);
def->second.set_defending(true,attack_type::LONG_RANGE);
const gamemap::location leader_loc = under_leadership(units,a);
unit_map::iterator leader = units.end();
if(leader_loc.valid()) {
@ -215,8 +213,8 @@ bool unit_attack_ranged(display& disp, unit_map& units, const gamemap& map,
//the missile frames are based around the time when the missile impacts.
//the 'real' frames are based around the time when the missile launches.
const int first_missile = minimum<int>(-100,attack.get_first_frame(attack_type::MISSILE_FRAME));
const int last_missile = attack.get_last_frame(attack_type::MISSILE_FRAME);
const int first_missile = minimum<int>(-100,attack.animation().get_first_frame(unit_animation::MISSILE_FRAME));
const int last_missile = attack.animation().get_last_frame(unit_animation::MISSILE_FRAME);
const int real_last_missile = last_missile - first_missile;
const int missile_impact = -first_missile;
@ -224,17 +222,17 @@ bool unit_attack_ranged(display& disp, unit_map& units, const gamemap& map,
const int time_resolution = 20;
const int acceleration = disp.turbo() ? 5:1;
const std::vector<attack_type::sfx>& sounds = attack.sound_effects();
std::vector<attack_type::sfx>::const_iterator sfx_it = sounds.begin();
const std::vector<unit_animation::sfx>& sounds = attack.animation().sound_effects();
std::vector<unit_animation::sfx>::const_iterator sfx_it = sounds.begin();
const std::string& hit_sound = def->second.type().get_hit_sound();
bool played_hit_sound = (hit_sound == "" || hit_sound == "null");
const int play_hit_sound_at = 0;
const bool hits = damage > 0;
const int begin_at = attack.get_first_frame();
const int begin_at = attack.animation().get_first_frame();
const int end_at = maximum((damage+1)*time_resolution+missile_impact,
maximum(attack.get_last_frame(),real_last_missile));
maximum(attack.animation().get_last_frame(),real_last_missile));
const double xsrc = disp.get_location_x(a);
const double ysrc = disp.get_location_y(a);
@ -246,8 +244,7 @@ bool unit_attack_ranged(display& disp, unit_map& units, const gamemap& map,
const bool vflip = b.y > a.y || b.y == a.y && is_even(a.x);
const bool hflip = b.x < a.x;
const attack_type::FRAME_DIRECTION dir =
(a.x == b.x) ? attack_type::VERTICAL:attack_type::DIAGONAL;
const unit_animation::FRAME_DIRECTION dir = (a.x == b.x) ? unit_animation::VERTICAL:unit_animation::DIAGONAL;
bool dead = false;
const int drain_speed = 1*acceleration;
@ -266,6 +263,8 @@ bool unit_attack_ranged(display& disp, unit_map& units, const gamemap& map,
for(int i = begin_at; i < end_at; i += time_resolution*acceleration) {
events::pump();
def->second.set_defending(true,hits,i - missile_impact,attack_type::LONG_RANGE);
//this is a while instead of an if, because there might be multiple
//sounds playing simultaneously or close together
while(!hide && sfx_it != sounds.end() && i >= sfx_it->time) {
@ -284,7 +283,7 @@ bool unit_attack_ranged(display& disp, unit_map& units, const gamemap& map,
const std::string* new_halo = NULL;
int new_halo_x = 0, new_halo_y = 0;
const std::string* unit_image = attack.get_frame(i,NULL,attack_type::UNIT_FRAME,attack_type::VERTICAL,&new_halo,&new_halo_x,&new_halo_y);
const std::string* unit_image = attack.animation().get_frame(i,NULL,unit_animation::UNIT_FRAME,unit_animation::VERTICAL,&new_halo,&new_halo_x,&new_halo_y);
if(att->second.facing_left() == false) {
new_halo_x *= -1;
}
@ -354,8 +353,8 @@ bool unit_attack_ranged(display& disp, unit_map& units, const gamemap& map,
const std::string* new_halo = NULL;
int new_halo_x = 0, new_halo_y = 0;
const std::string* missile_image = attack.get_frame(missile_frame,NULL,
attack_type::MISSILE_FRAME,dir,&new_halo,&new_halo_x,&new_halo_y);
const std::string* missile_image = attack.animation().get_frame(missile_frame,NULL,
unit_animation::MISSILE_FRAME,dir,&new_halo,&new_halo_x,&new_halo_y);
if(att->second.facing_left() == false) {
new_halo_x *= -1;
@ -367,7 +366,7 @@ bool unit_attack_ranged(display& disp, unit_map& units, const gamemap& map,
static const std::string default_missile(game_config::missile_n_image);
static const std::string default_diag_missile(game_config::missile_ne_image);
if(missile_image == NULL) {
if(dir == attack_type::VERTICAL)
if(dir == unit_animation::VERTICAL)
missile_image = &default_missile;
else
missile_image = &default_diag_missile;
@ -523,8 +522,8 @@ bool unit_attack(display& disp, unit_map& units, const gamemap& map,
}
const bool hits = damage > 0;
const std::vector<attack_type::sfx>& sounds = attack.sound_effects();
std::vector<attack_type::sfx>::const_iterator sfx_it = sounds.begin();
const std::vector<unit_animation::sfx>& sounds = attack.animation().sound_effects();
std::vector<unit_animation::sfx>::const_iterator sfx_it = sounds.begin();
const std::string& hit_sound = def->second.type().get_hit_sound();
bool played_hit_sound = (hit_sound == "" || hit_sound == "null");
@ -544,9 +543,9 @@ bool unit_attack(display& disp, unit_map& units, const gamemap& map,
leader->second.set_leading(true);
}
const int begin_at = minimum<int>(-200,attack.get_first_frame());
const int begin_at = minimum<int>(-200,attack.animation().get_first_frame());
const int end_at = maximum<int>((damage+1)*time_resolution,
maximum<int>(200,attack.get_last_frame()));
maximum<int>(200,attack.animation().get_last_frame()));
const double xsrc = disp.get_location_x(a);
const double ysrc = disp.get_location_y(a);
@ -583,6 +582,8 @@ bool unit_attack(display& disp, unit_map& units, const gamemap& map,
for(int i = begin_at; i < end_at; i += time_resolution*acceleration) {
events::pump();
def->second.set_defending(true,hits,i,attack_type::SHORT_RANGE);
//this is a while instead of an if, because there might be multiple
//sounds playing simultaneously or close together
while(!hide && sfx_it != sounds.end() && i >= sfx_it->time) {
@ -627,7 +628,6 @@ bool unit_attack(display& disp, unit_map& units, const gamemap& map,
++flash_num;
}
disp.draw_tile(b.x,b.y,NULL,defender_alpha,defender_colour);
if(leader_loc.valid()) {
disp.draw_tile(leader_loc.x,leader_loc.y);
@ -637,8 +637,8 @@ bool unit_attack(display& disp, unit_map& units, const gamemap& map,
int new_halo_x = 0, new_halo_y = 0;
int xoffset = 0;
const std::string* unit_image = attack.get_frame(i,&xoffset,attack_type::UNIT_FRAME,attack_type::VERTICAL,
&new_halo_image,&new_halo_x,&new_halo_y);
const std::string* unit_image = attack.animation().get_frame(i,&xoffset,unit_animation::UNIT_FRAME,unit_animation::VERTICAL,
&new_halo_image,&new_halo_x,&new_halo_y);
if(!attacker.facing_left()) {
xoffset *= -1;

View File

@ -28,28 +28,8 @@
#include <unistd.h>
#endif
attack_type::attack_type(const config& cfg)
unit_animation::unit_animation(const config& cfg)
{
name_ = cfg["name"];
type_ = cfg["type"];
special_ = cfg["special"];
backstab_ = special_ == "backstab";
icon_ = cfg["icon"];
if(icon_.empty())
icon_ = "attacks/" + name_ + ".png";
range_ = cfg["range"] == "long" ? LONG_RANGE : SHORT_RANGE;
hexes_ = maximum<int>(1,atoi(cfg["hexes"].c_str()));
damage_ = atol(cfg["damage"].c_str());
num_attacks_ = atol(cfg["number"].c_str());
attack_weight_ = atof(cfg["attack_weight"].c_str());
defense_weight_ = atof(cfg["defense_weight"].c_str());
if ( ! attack_weight_ )
attack_weight_ = 1.0;
if ( ! defense_weight_ )
defense_weight_ = 1.0;
config::const_child_itors range = cfg.child_range("frame");
for(; range.first != range.second; ++range.first){
const int beg = atoi((**range.first)["begin"].c_str());
@ -97,6 +77,92 @@ attack_type::attack_type(const config& cfg)
}
}
int unit_animation::get_first_frame(unit_animation::FRAME_TYPE type) const
{
if(frames_[type].empty())
return 0;
else
return minimum<int>(frames_[type].front().start,0);
}
int unit_animation::get_last_frame(unit_animation::FRAME_TYPE type) const
{
if(frames_[type].empty())
return 0;
else
return maximum<int>(frames_[type].back().end,0);
}
const std::string* unit_animation::get_frame(int milliseconds, int* xoff,
unit_animation::FRAME_TYPE type,
unit_animation::FRAME_DIRECTION dir,
const std::string** halo, int* halo_x, int* halo_y) const
{
for(std::vector<frame>::const_iterator i = frames_[type].begin();
i != frames_[type].end(); ++i) {
if(i->start > milliseconds)
return NULL;
if(i->start <= milliseconds && i->end > milliseconds) {
if(xoff != NULL) {
*xoff = i->xoffset;
}
if(halo != NULL) {
if(i->halo.empty()) {
*halo = NULL;
} else {
*halo = &i->halo;
}
if(halo_x != NULL) {
*halo_x = i->halo_x;
}
if(halo_y != NULL) {
*halo_y = i->halo_y;
}
}
if(dir == DIAGONAL && i->image_diagonal != "") {
return &i->image_diagonal;
} else {
return &i->image;
}
}
}
return NULL;
}
const std::vector<unit_animation::sfx>& unit_animation::sound_effects() const
{
return sfx_;
}
attack_type::attack_type(const config& cfg) : animation_(cfg)
{
name_ = cfg["name"];
type_ = cfg["type"];
special_ = cfg["special"];
backstab_ = special_ == "backstab";
icon_ = cfg["icon"];
if(icon_.empty())
icon_ = "attacks/" + name_ + ".png";
range_ = cfg["range"] == "long" ? LONG_RANGE : SHORT_RANGE;
hexes_ = maximum<int>(1,atoi(cfg["hexes"].c_str()));
damage_ = atol(cfg["damage"].c_str());
num_attacks_ = atol(cfg["number"].c_str());
attack_weight_ = atof(cfg["attack_weight"].c_str());
defense_weight_ = atof(cfg["defense_weight"].c_str());
if ( ! attack_weight_ )
attack_weight_ = 1.0;
if ( ! defense_weight_ )
defense_weight_ = 1.0;
}
const std::string& attack_type::name() const
{
return name_;
@ -152,69 +218,6 @@ bool attack_type::backstab() const
return backstab_;
}
int attack_type::get_first_frame(attack_type::FRAME_TYPE type) const
{
if(frames_[type].empty())
return 0;
else
return minimum<int>(frames_[type].front().start,0);
}
int attack_type::get_last_frame(attack_type::FRAME_TYPE type) const
{
if(frames_[type].empty())
return 0;
else
return maximum<int>(frames_[type].back().end,0);
}
const std::string* attack_type::get_frame(int milliseconds, int* xoff,
attack_type::FRAME_TYPE type,
attack_type::FRAME_DIRECTION dir,
const std::string** halo, int* halo_x, int* halo_y) const
{
for(std::vector<frame>::const_iterator i = frames_[type].begin();
i != frames_[type].end(); ++i) {
if(i->start > milliseconds)
return NULL;
if(i->start <= milliseconds && i->end > milliseconds) {
if(xoff != NULL) {
*xoff = i->xoffset;
}
if(halo != NULL) {
if(i->halo.empty()) {
*halo = NULL;
} else {
*halo = &i->halo;
}
if(halo_x != NULL) {
*halo_x = i->halo_x;
}
if(halo_y != NULL) {
*halo_y = i->halo_y;
}
}
if(dir == DIAGONAL && i->image_diagonal != "") {
return &i->image_diagonal;
} else {
return &i->image;
}
}
}
return NULL;
}
const std::vector<attack_type::sfx>& attack_type::sound_effects() const
{
return sfx_;
}
bool attack_type::matches_filter(const config& cfg) const
{
const std::string& filter_range = cfg["range"];
@ -549,6 +552,11 @@ unit_type::unit_type(const config& cfg, const movement_type_map& mv_types,
}
can_advance_ = advances_to().empty() == false;
const config::child_list& defends = cfg_.get_children("defend");
for(config::child_list::const_iterator d = defends.begin(); d != defends.end(); ++d) {
defensive_animations_.push_back(defensive_animation(**d));
}
}
int unit_type::num_traits() const { return race_->num_traits(); }
@ -868,6 +876,39 @@ const std::string& unit_type::race() const
return race_->name();
}
unit_type::defensive_animation::defensive_animation(const config& cfg) : hits(HIT_OR_MISS), range(SHORT_OR_LONG), animation(cfg)
{
const std::string& hits_str = cfg["hits"];
if(hits_str.empty() == false) {
hits = (hits_str == "yes") ? HIT : MISS;
}
const std::string& range_str = cfg["range"];
if(range_str.empty() == false) {
range = (range_str == "short") ? SHORT : LONG;
}
}
bool unit_type::defensive_animation::matches(bool h, attack_type::RANGE r) const
{
if(hits == HIT && h == false || hits == MISS && h == true || range == SHORT && r == attack_type::LONG_RANGE || range == LONG && r == attack_type::SHORT_RANGE) {
return false;
} else {
return true;
}
}
const unit_animation* unit_type::defend_animation(bool hits, attack_type::RANGE range) const
{
for(std::vector<defensive_animation>::const_iterator i = defensive_animations_.begin(); i != defensive_animations_.end(); ++i) {
if(i->matches(hits,range)) {
return &i->animation;
}
}
return NULL;
}
game_data::game_data(const config& cfg)
{
static const std::vector<config*> dummy_traits;

View File

@ -21,26 +21,11 @@
#include <string>
#include <vector>
//the 'attack type' is the type of attack, how many times it strikes,
//and how much damage it does.
class attack_type
//a class to describe a unit's animation sequence
class unit_animation
{
public:
enum RANGE { SHORT_RANGE, LONG_RANGE };
attack_type(const config& cfg);
const std::string& name() const;
const std::string& type() const;
const std::string& special() const;
const std::string& icon() const;
RANGE range() const;
int hexes() const;
int damage() const;
int num_attacks() const;
double attack_weight() const;
double defense_weight() const;
bool backstab() const;
unit_animation(const config& cfg);
enum FRAME_TYPE { UNIT_FRAME, MISSILE_FRAME };
enum FRAME_DIRECTION { VERTICAL, DIAGONAL };
@ -64,24 +49,7 @@ public:
const std::vector<sfx>& sound_effects() const;
bool matches_filter(const config& cfg) const;
bool apply_modification(const config& cfg,std::string* description);
private:
std::string name_;
std::string type_;
std::string special_;
std::string icon_;
RANGE range_;
int hexes_;
int damage_;
int num_attacks_;
double attack_weight_;
double defense_weight_;
//caches whether the unit can backstab. This is important
//because the AI queries it alot.
bool backstab_;
struct frame {
frame(int i1, int i2, const std::string& img, const std::string& halo, int offset, int halo_x, int halo_y)
: start(i1), end(i2), xoffset(offset), image(img), halo(halo), halo_x(halo_x), halo_y(halo_y)
@ -106,6 +74,49 @@ private:
std::vector<sfx> sfx_;
};
//the 'attack type' is the type of attack, how many times it strikes,
//and how much damage it does.
class attack_type
{
public:
enum RANGE { SHORT_RANGE, LONG_RANGE };
attack_type(const config& cfg);
const std::string& name() const;
const std::string& type() const;
const std::string& special() const;
const std::string& icon() const;
RANGE range() const;
int hexes() const;
int damage() const;
int num_attacks() const;
double attack_weight() const;
double defense_weight() const;
bool backstab() const;
const unit_animation& animation() const { return animation_; }
bool matches_filter(const config& cfg) const;
bool apply_modification(const config& cfg,std::string* description);
private:
unit_animation animation_;
std::string name_;
std::string type_;
std::string special_;
std::string icon_;
RANGE range_;
int hexes_;
int damage_;
int num_attacks_;
double attack_weight_;
double defense_weight_;
//caches whether the unit can backstab. This is important
//because the AI queries it alot.
bool backstab_;
};
class unit_movement_type;
//the 'unit movement type' is the basic size of the unit - flying, small land,
@ -220,6 +231,8 @@ public:
const std::string& race() const;
const unit_animation* defend_animation(bool hits, attack_type::RANGE range) const;
private:
const config& cfg_;
@ -244,6 +257,18 @@ private:
const std::vector<config*>& possibleTraits_;
unit_race::GENDER gender_;
struct defensive_animation
{
defensive_animation(const config& cfg);
bool matches(bool hits, attack_type::RANGE range) const;
enum { HIT, MISS, HIT_OR_MISS } hits;
enum { SHORT, LONG, SHORT_OR_LONG } range;
unit_animation animation;
};
std::vector<defensive_animation> defensive_animations_;
};
struct game_data