Added Elvish Shaman's heal animation
This commit is contained in:
parent
a8409c19d1
commit
0debf0d593
16 changed files with 100 additions and 22 deletions
|
@ -60,8 +60,10 @@ Defeat:
|
|||
y=23
|
||||
[/unit]
|
||||
[ai]
|
||||
avoid_x=20-31
|
||||
avoid_y=1-12
|
||||
[avoid]
|
||||
x=20-31
|
||||
y=1-12
|
||||
[/avoid]
|
||||
village_value=0.0
|
||||
leader_value=0.0
|
||||
[target]
|
||||
|
|
|
@ -49,7 +49,11 @@ Defeat:
|
|||
canrecruit=1
|
||||
recruit=Vampire Bat,Walking Corpse,Dark Adept
|
||||
[ai]
|
||||
recruitment_pattern=fighter,fighter,fighter,fighter,fighter,fighter,fighter,archer
|
||||
recruitment_pattern=fighter,fighter,fighter,fighter,fighter,fighter,fighter,archer
|
||||
grouping=no
|
||||
aggression=1.0
|
||||
caution=-1.0
|
||||
simple_targetting=yes
|
||||
[/ai]
|
||||
{GOLD 240 300 400}
|
||||
enemy=1
|
||||
|
|
|
@ -4,7 +4,8 @@ race=elf
|
|||
gender=female
|
||||
image=elvish-shaman.png
|
||||
image_defensive=elvish-shaman-defend.png
|
||||
image_healing=elvish-shaman-healing.png
|
||||
image_healing=null
|
||||
image_halo_healing=elvish-shaman-heal1.png:70,elvish-shaman-heal2.png:70,elvish-shaman-heal3.png:70,elvish-shaman-heal4.png:70,elvish-shaman-heal5.png:70
|
||||
hitpoints=26
|
||||
ability=heals
|
||||
movement_type=woodland
|
||||
|
|
BIN
images/elvish-shaman-heal1.png
Normal file
BIN
images/elvish-shaman-heal1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 990 B |
BIN
images/elvish-shaman-heal2.png
Normal file
BIN
images/elvish-shaman-heal2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
BIN
images/elvish-shaman-heal3.png
Normal file
BIN
images/elvish-shaman-heal3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
images/elvish-shaman-heal4.png
Normal file
BIN
images/elvish-shaman-heal4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
BIN
images/elvish-shaman-heal5.png
Normal file
BIN
images/elvish-shaman-heal5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
|
@ -1118,6 +1118,12 @@ void calculate_healing(display& disp, const gamestatus& status, const gamemap& m
|
|||
unit& healer = units.find(i->second)->second;
|
||||
healer.set_healing(true);
|
||||
|
||||
const std::string& halo_image = healer.type().image_halo_healing();
|
||||
if(halo_image.empty() == false) {
|
||||
halo::add(disp.get_location_x(i->second)+disp.hex_size()/2,disp.get_location_y(i->second)+disp.hex_size()/2,
|
||||
halo_image,healer.facing_left() ? halo::NORMAL : halo::REVERSE,1);
|
||||
}
|
||||
|
||||
disp.draw_tile(i->second.x,i->second.y);
|
||||
}
|
||||
|
||||
|
|
50
src/ai.cpp
50
src/ai.cpp
|
@ -1281,7 +1281,7 @@ void ai::analyze_potential_recruit_movements()
|
|||
|
||||
std::cerr << "targets: " << targets.size() << "\n";
|
||||
|
||||
int best_score = -1;
|
||||
std::map<std::string,int> best_scores;
|
||||
|
||||
for(std::set<std::string>::const_iterator i = recruits.begin(); i != recruits.end(); ++i) {
|
||||
const game_data::unit_type_map::const_iterator info = gameinfo_.unit_types.find(*i);
|
||||
|
@ -1318,13 +1318,21 @@ void ai::analyze_potential_recruit_movements()
|
|||
const int average_cost = cost/targets_reached;
|
||||
const int score = (average_cost * (targets_reached+targets_missed))/targets_reached;
|
||||
unit_movement_scores_[*i] = score;
|
||||
if(best_score == -1 || score < best_score) {
|
||||
best_score = score;
|
||||
|
||||
const std::map<std::string,int>::const_iterator current_best = best_scores.find(temp_unit.type().usage());
|
||||
if(current_best == best_scores.end() || score < current_best->second) {
|
||||
best_scores[temp_unit.type().usage()] = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(std::map<std::string,int>::iterator j = unit_movement_scores_.begin(); j != unit_movement_scores_.end(); ++j) {
|
||||
const game_data::unit_type_map::const_iterator info = gameinfo_.unit_types.find(j->first);
|
||||
if(info == gameinfo_.unit_types.end()) {
|
||||
}
|
||||
continue;
|
||||
|
||||
const int best_score = best_scores[info->second.usage()];
|
||||
if(best_score > 0) {
|
||||
j->second = (j->second*10)/best_score;
|
||||
if(j->second > 15) {
|
||||
|
@ -1515,7 +1523,7 @@ void ai::move_leader_after_recruit(const move_map& enemy_dstsrc)
|
|||
std::cerr << "moving leader after recruit...\n";
|
||||
|
||||
const unit_map::iterator leader = find_leader(units_,team_num_);
|
||||
if(leader == units_.end() || leader->second.stone()) {
|
||||
if(leader == units_.end() || leader->second.incapacitated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1599,6 +1607,29 @@ void ai::move_leader_after_recruit(const move_map& enemy_dstsrc)
|
|||
}
|
||||
}
|
||||
|
||||
bool ai::leader_can_reach_keep() const
|
||||
{
|
||||
const unit_map::iterator leader = find_leader(units_,team_num_);
|
||||
if(leader == units_.end() || leader->second.incapacitated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const gamemap::location& start_pos = nearest_keep(leader->first);
|
||||
if(start_pos.valid() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(leader->first == start_pos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//find where the leader can move
|
||||
const paths leader_paths(map_,state_,gameinfo_,units_,leader->first,teams_,false,false);
|
||||
|
||||
|
||||
return leader_paths.routes.count(start_pos) > 0;
|
||||
}
|
||||
|
||||
int ai::rate_terrain(const unit& u, const gamemap::location& loc)
|
||||
{
|
||||
const gamemap::TERRAIN terrain = map_.get_terrain(loc);
|
||||
|
@ -1741,12 +1772,13 @@ const gamemap::location& ai::nearest_keep(const gamemap::location& loc) const
|
|||
const std::set<gamemap::location>& ai::avoided_locations() const
|
||||
{
|
||||
if(avoid_.empty()) {
|
||||
const std::string& xrange = current_team().ai_parameters()["avoid_x"];
|
||||
const std::string& yrange = current_team().ai_parameters()["avoid_y"];
|
||||
const config::child_list& avoids = current_team().ai_parameters().get_children("avoid");
|
||||
for(config::child_list::const_iterator a = avoids.begin(); a != avoids.end(); ++a) {
|
||||
|
||||
const std::vector<location>& locs = parse_location_range(xrange,yrange);
|
||||
for(std::vector<location>::const_iterator i = locs.begin(); i != locs.end(); ++i) {
|
||||
avoid_.insert(*i);
|
||||
const std::vector<location>& locs = parse_location_range((**a)["x"],(**a)["y"]);
|
||||
for(std::vector<location>::const_iterator i = locs.begin(); i != locs.end(); ++i) {
|
||||
avoid_.insert(*i);
|
||||
}
|
||||
}
|
||||
|
||||
if(avoid_.empty()) {
|
||||
|
|
|
@ -324,6 +324,8 @@ public:
|
|||
const move_map& enemy_dstsrc, const move_map& enemy_srcdst) const;
|
||||
void invalidate_defensive_position_cache();
|
||||
|
||||
bool leader_can_reach_keep() const;
|
||||
|
||||
protected:
|
||||
|
||||
mutable std::map<location,defensive_position> defensive_position_cache_;
|
||||
|
|
|
@ -536,6 +536,12 @@ double ai::attack_analysis::rating(double aggression, ai& ai_obj) const
|
|||
|
||||
value -= exposure;
|
||||
}
|
||||
|
||||
//if this attack uses our leader, and the leader can reach the keep, and has gold to spend, reduce
|
||||
//the value to reflect the leader's lost recruitment opportunity in the case of an attack
|
||||
if(uses_leader && ai_obj.leader_can_reach_keep() && ai_obj.current_team().gold() > 20) {
|
||||
value -= double(ai_obj.current_team().gold())*0.5;
|
||||
}
|
||||
|
||||
//prefer to attack already damaged targets
|
||||
value += ((target_starting_damage/3 + avg_damage_inflicted) - (1.0-aggression)*avg_damage_taken)/10.0;
|
||||
|
|
33
src/halo.cpp
33
src/halo.cpp
|
@ -16,12 +16,14 @@ display* disp = NULL;
|
|||
class effect
|
||||
{
|
||||
public:
|
||||
effect(int xpos, int ypos, const std::string& img);
|
||||
effect(int xpos, int ypos, const std::string& img, ORIENTATION orientation, int lifetime);
|
||||
|
||||
void set_location(int x, int y);
|
||||
|
||||
void render();
|
||||
void unrender();
|
||||
|
||||
bool expired() const;
|
||||
private:
|
||||
|
||||
const std::string& current_image();
|
||||
|
@ -50,7 +52,8 @@ private:
|
|||
std::vector<frame> images_;
|
||||
std::string current_image_;
|
||||
|
||||
int start_cycle_, cycle_time_;
|
||||
bool reverse_;
|
||||
int start_cycle_, cycle_time_, lifetime_;
|
||||
|
||||
int x_, y_;
|
||||
double zoom_;
|
||||
|
@ -65,8 +68,8 @@ bool hide_halo = false;
|
|||
|
||||
static const SDL_Rect empty_rect = {0,0,0,0};
|
||||
|
||||
effect::effect(int xpos, int ypos, const std::string& img)
|
||||
: start_cycle_(-1), cycle_time_(50), x_(xpos), y_(ypos), zoom_(disp->zoom()), surf_(NULL), buffer_(NULL), rect_(empty_rect)
|
||||
effect::effect(int xpos, int ypos, const std::string& img, ORIENTATION orientation, int lifetime)
|
||||
: reverse_(orientation == REVERSE), start_cycle_(-1), cycle_time_(50), lifetime_(lifetime), x_(xpos), y_(ypos), zoom_(disp->zoom()), surf_(NULL), buffer_(NULL), rect_(empty_rect)
|
||||
{
|
||||
if(std::find(img.begin(),img.end(),',') != img.end()) {
|
||||
const std::vector<std::string>& imgs = config::split(img,',');
|
||||
|
@ -125,6 +128,10 @@ void effect::rezoom()
|
|||
zoom_ = new_zoom;
|
||||
|
||||
surf_.assign(image::get_image(current_image_,image::UNSCALED));
|
||||
if(surf_ != NULL && reverse_) {
|
||||
surf_.assign(image::reverse_image(surf_));
|
||||
}
|
||||
|
||||
if(surf_ != NULL && zoom_ != 1.0) {
|
||||
surf_.assign(scale_surface(surf_,int(surf_->w*zoom_),int(surf_->h*zoom_)));
|
||||
}
|
||||
|
@ -196,6 +203,11 @@ void effect::unrender()
|
|||
update_rect(rect_);
|
||||
}
|
||||
|
||||
bool effect::expired() const
|
||||
{
|
||||
return lifetime_ >= 0 && start_cycle_ >= 0 && SDL_GetTicks() - start_cycle_ > lifetime_*cycle_time_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
manager::manager(display& screen) : old(disp)
|
||||
|
@ -219,10 +231,10 @@ halo_hider::~halo_hider()
|
|||
hide_halo = old;
|
||||
}
|
||||
|
||||
int add(int x, int y, const std::string& image)
|
||||
int add(int x, int y, const std::string& image, ORIENTATION orientation, int lifetime_cycles)
|
||||
{
|
||||
const int id = halo_id++;
|
||||
haloes.insert(std::pair<int,effect>(id,effect(x,y,image)));
|
||||
haloes.insert(std::pair<int,effect>(id,effect(x,y,image,orientation,lifetime_cycles)));
|
||||
return id;
|
||||
}
|
||||
|
||||
|
@ -245,8 +257,13 @@ void render()
|
|||
return;
|
||||
}
|
||||
|
||||
for(std::map<int,effect>::iterator i = haloes.begin(); i != haloes.end(); ++i) {
|
||||
i->second.render();
|
||||
for(std::map<int,effect>::iterator i = haloes.begin(); i != haloes.end(); ) {
|
||||
if(i->second.expired()) {
|
||||
haloes.erase(i++);
|
||||
} else {
|
||||
i->second.render();
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,12 @@ private:
|
|||
bool old;
|
||||
};
|
||||
|
||||
enum ORIENTATION { NORMAL, REVERSE };
|
||||
|
||||
///function to add a haloing effect using 'image'
|
||||
///centered on (x,y)
|
||||
///returns the handle to the halo object
|
||||
int add(int x, int y, const std::string& image);
|
||||
int add(int x, int y, const std::string& image, ORIENTATION orientation=NORMAL, int lifetime_cycles=-1);
|
||||
|
||||
///function to set the position of an existing haloing
|
||||
///effect, according to its handle
|
||||
|
|
|
@ -671,6 +671,11 @@ const std::string& unit_type::image_healing() const
|
|||
}
|
||||
}
|
||||
|
||||
const std::string& unit_type::image_halo_healing() const
|
||||
{
|
||||
return cfg_["image_halo_healing"];
|
||||
}
|
||||
|
||||
const std::string& unit_type::image_profile() const
|
||||
{
|
||||
const std::string& val = cfg_["profile"];
|
||||
|
|
|
@ -166,6 +166,7 @@ public:
|
|||
const std::string& image_defensive(attack_type::RANGE range) const;
|
||||
const std::string& image_leading() const;
|
||||
const std::string& image_healing() const;
|
||||
const std::string& image_halo_healing() const;
|
||||
const std::string& unit_description() const;
|
||||
const std::string& get_hit_sound() const;
|
||||
const std::string& die_sound() const;
|
||||
|
|
Loading…
Add table
Reference in a new issue