code refactoring - simplified the 'display' module

This commit is contained in:
Dave White 2004-04-22 18:58:59 +00:00
parent ab12425061
commit accbc0f772
26 changed files with 876 additions and 798 deletions

View file

@ -25,6 +25,7 @@
#include "replay.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "unit_display.hpp"
#include "util.hpp"
#include <cmath>
@ -577,7 +578,7 @@ void attack(display& gui, const gamemap& map,
}
}
bool dies = gui.unit_attack(attacker,defender,
bool dies = unit_display::unit_attack(gui,units,map,attacker,defender,
hits ? stats.damage_defender_takes : 0,
a->second.attacks()[attack_with]);
@ -711,7 +712,7 @@ void attack(display& gui, const gamemap& map,
}
}
bool dies = gui.unit_attack(defender,attacker,
bool dies = unit_display::unit_attack(gui,units,map,defender,attacker,
hits ? stats.damage_attacker_takes : 0,
d->second.attacks()[stats.defend_with]);
@ -828,10 +829,10 @@ void attack(display& gui, const gamemap& map,
gui.invalidate_unit();
}
int tower_owner(const gamemap::location& loc, std::vector<team>& teams)
int village_owner(const gamemap::location& loc, std::vector<team>& teams)
{
for(size_t i = 0; i != teams.size(); ++i) {
if(teams[i].owns_tower(loc))
if(teams[i].owns_village(loc))
return i;
}
@ -839,10 +840,10 @@ int tower_owner(const gamemap::location& loc, std::vector<team>& teams)
}
void get_tower(const gamemap::location& loc, std::vector<team>& teams,
size_t team_num, const unit_map& units)
void get_village(const gamemap::location& loc, std::vector<team>& teams,
size_t team_num, const unit_map& units)
{
if(team_num < teams.size() && teams[team_num].owns_tower(loc)) {
if(team_num < teams.size() && teams[team_num].owns_village(loc)) {
return;
}
@ -853,7 +854,7 @@ void get_tower(const gamemap::location& loc, std::vector<team>& teams,
for(std::vector<team>::iterator i = teams.begin(); i != teams.end(); ++i) {
const int side = i - teams.begin() + 1;
if(team_num >= teams.size() || has_leader || teams[team_num].is_enemy(side)) {
i->lose_tower(loc);
i->lose_village(loc);
}
}
@ -862,7 +863,7 @@ void get_tower(const gamemap::location& loc, std::vector<team>& teams,
}
if(has_leader) {
teams[team_num].get_tower(loc);
teams[team_num].get_village(loc);
}
}
@ -951,7 +952,7 @@ void calculate_healing(display& disp, const gamemap& map,
for(i = units.begin(); i != units.end(); ++i) {
amount_healed = 0;
//the unit heals if it's on this side, and it's on a tower or
//the unit heals if it's on this side, and it's on a village or
//it has regeneration, and it is wounded
if(i->second.side() == side) {
if(i->second.hitpoints() < i->second.max_hitpoints()){
@ -1191,7 +1192,7 @@ void check_victory(std::map<gamemap::location,unit>& units,
//clear villages for teams that have no leader
for(std::vector<team>::iterator tm = teams.begin(); tm != teams.end(); ++tm) {
if(std::find(seen_leaders.begin(),seen_leaders.end(),tm-teams.begin() + 1) == seen_leaders.end()) {
tm->clear_towers();
tm->clear_villages();
}
}
@ -1526,8 +1527,9 @@ size_t move_unit(display* disp, const game_data& gamedata,
}
units.erase(ui);
if(disp != NULL)
disp->move_unit(steps,u);
if(disp != NULL) {
unit_display::move_unit(*disp,map,steps,u);
}
u.set_movement(moves_left);
@ -1537,12 +1539,12 @@ size_t move_unit(display* disp, const game_data& gamedata,
disp->invalidate(steps.back());
}
int orig_tower_owner = -1;
int orig_village_owner = -1;
if(map.is_village(steps.back())) {
orig_tower_owner = tower_owner(steps.back(),teams);
orig_village_owner = village_owner(steps.back(),teams);
if(orig_tower_owner != team_num) {
get_tower(steps.back(),teams,team_num,units);
if(orig_village_owner != team_num) {
get_village(steps.back(),teams,team_num,units);
ui->second.set_movement(0);
}
}
@ -1553,7 +1555,7 @@ size_t move_unit(display* disp, const game_data& gamedata,
if(event_mutated || should_clear_stack) {
undo_stack->clear();
} else {
undo_stack->push_back(undo_action(steps,starting_moves,orig_tower_owner));
undo_stack->push_back(undo_action(steps,starting_moves,orig_village_owner));
}
}

View file

@ -98,11 +98,11 @@ void attack(display& gui, const gamemap& map,
//given the location of a village, will return the 0-based index of the team
//that currently owns it, and -1 if it is unowned.
int tower_owner(const gamemap::location& loc, std::vector<team>& teams);
int village_owner(const gamemap::location& loc, std::vector<team>& teams);
//makes it so the tower at the given location is owned by the given
//makes it so the village at the given location is owned by the given
//0-based team number
void get_tower(const gamemap::location& loc, std::vector<team>& teams,
void get_village(const gamemap::location& loc, std::vector<team>& teams,
size_t team_num, const unit_map& units);
//given the 1-based side, will find the leader of that side,

View file

@ -23,6 +23,7 @@
#include "replay.hpp"
#include "show_dialog.hpp"
#include "statistics.hpp"
#include "unit_display.hpp"
#include <iostream>
@ -219,14 +220,14 @@ gamemap::location ai_interface::move_unit(location from, location to, std::map<l
}
steps.push_back(to); //add the destination to the steps
info_.disp.move_unit(steps,current_unit);
unit_display::move_unit(info_.disp,info_.map,steps,current_unit);
}
}
current_unit.set_movement(0);
info_.units.insert(std::pair<location,unit>(to,current_unit));
if(info_.map.is_village(to)) {
get_tower(to,info_.teams,info_.team_num-1,info_.units);
get_village(to,info_.teams,info_.team_num-1,info_.units);
}
info_.disp.draw_tile(to.x,to.y);
@ -295,7 +296,7 @@ void ai_interface::calculate_possible_moves(std::map<location,paths>& res, move_
//don't take friendly villages
if(!enemy && info_.map.is_village(dst)) {
for(size_t n = 0; n != info_.teams.size(); ++n) {
if(info_.teams[n].owns_tower(dst)) {
if(info_.teams[n].owns_village(dst)) {
if(n+1 != info_.team_num && current_team().is_enemy(n+1) == false) {
friend_owns = true;
}
@ -508,17 +509,17 @@ void ai_interface::attack_enemy(const location& u, const location& target, int w
bool ai::get_villages(std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc, unit_map::const_iterator leader)
{
//try to acquire towers
//try to acquire villages
for(move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) {
if(map_.is_village(i->first) == false) {
continue;
}
bool want_tower = true, owned = false;
bool want_village = true, owned = false;
for(size_t j = 0; j != teams_.size(); ++j) {
owned = teams_[j].owns_tower(i->first);
owned = teams_[j].owns_village(i->first);
if(owned && !current_team().is_enemy(j+1)) {
want_tower = false;
want_village = false;
}
if(owned) {
@ -526,12 +527,12 @@ bool ai::get_villages(std::map<gamemap::location,paths>& possible_moves, const m
}
}
//if it's a neutral tower, and we have no leader, then the village is no use to us,
//if it's a neutral village, and we have no leader, then the village is no use to us,
//and we don't want it.
if(!owned && leader == units_.end())
want_tower = false;
want_village = false;
if(want_tower) {
if(want_village) {
std::cerr << "trying to acquire village: " << i->first.x
<< ", " << i->first.y << "\n";
@ -775,13 +776,13 @@ void ai::do_recruitment()
//currently just spend all the gold we can!
const int min_gold = 0;
const int towers = map_.towers().size();
int taken_towers = 0;
const int villages = map_.villages().size();
int taken_villages = 0;
for(size_t j = 0; j != teams_.size(); ++j) {
taken_towers += teams_[j].towers().size();
taken_villages += teams_[j].villages().size();
}
const int neutral_towers = towers - taken_towers;
const int neutral_villages = villages - taken_villages;
//the villages per scout parameter is assumed to be based on a 2-side battle.
//in a greater than 2 side battle, we want to recruit less scouts, since the villages
@ -789,7 +790,7 @@ void ai::do_recruitment()
const int villages_per_scout = (current_team().villages_per_scout()*teams_.size())/2;
//get scouts depending on how many neutral villages there are
int scouts_wanted = villages_per_scout > 0 ? neutral_towers/villages_per_scout : 0;
int scouts_wanted = villages_per_scout > 0 ? neutral_villages/villages_per_scout : 0;
std::map<std::string,int> unit_types;
while(unit_types["scout"] < scouts_wanted) {
@ -898,7 +899,7 @@ void ai::move_leader_after_recruit(const move_map& enemy_dstsrc)
}
//search through villages finding one to capture
const std::vector<gamemap::location>& villages = map_.towers();
const std::vector<gamemap::location>& villages = map_.villages();
for(std::vector<gamemap::location>::const_iterator v = villages.begin();
v != villages.end(); ++v) {
const paths::routes_map::const_iterator itor = leader_paths.routes.find(*v);
@ -906,7 +907,7 @@ void ai::move_leader_after_recruit(const move_map& enemy_dstsrc)
continue;
}
const int owner = tower_owner(*v,teams_);
const int owner = village_owner(*v,teams_);
if(owner == -1 || current_team().is_enemy(owner+1) || leader->second.hitpoints() < leader->second.max_hitpoints()) {
//check that no enemies can reach the village
@ -944,11 +945,11 @@ int ai::rate_terrain(const unit& u, const gamemap::location& loc)
}
if(map_.is_village(terrain)) {
const int village_owner = tower_owner(loc,teams_);
const int owner = village_owner(loc,teams_);
if(village_owner == team_num_) {
if(owner == team_num_) {
rating += friendly_village_value;
} else if(village_owner == -1) {
} else if(owner == -1) {
rating += neutral_village_value;
} else {
rating += enemy_village_value;

View file

@ -218,7 +218,7 @@ private:
calculate_possible_moves(possible_moves,srcdst,dstsrc,false);
for(move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) {
if(get_info().map.is_village(i->first) && current_team().owns_tower(i->first) == false) {
if(get_info().map.is_village(i->first) && current_team().owns_village(i->first) == false) {
move_unit(i->second,i->first,possible_moves);
get_villages();
return;

View file

@ -125,18 +125,18 @@ std::vector<ai::target> ai::find_targets(unit_map::const_iterator leader, const
}
if(has_leader && current_team().village_value() > 0.0) {
const std::vector<location>& towers = map_.towers();
for(std::vector<location>::const_iterator t = towers.begin(); t != towers.end(); ++t) {
const std::vector<location>& villages = map_.villages();
for(std::vector<location>::const_iterator t = villages.begin(); t != villages.end(); ++t) {
assert(map_.on_board(*t));
bool get_tower = true;
bool get_village = true;
for(size_t i = 0; i != teams_.size(); ++i) {
if(!current_team().is_enemy(i+1) && teams_[i].owns_tower(*t)) {
get_tower = false;
if(!current_team().is_enemy(i+1) && teams_[i].owns_village(*t)) {
get_village = false;
break;
}
}
if(get_tower) {
if(get_village) {
targets.push_back(target(*t,current_team().village_value()));
}
}

View file

@ -27,6 +27,7 @@
#include "sound.hpp"
#include "team.hpp"
#include "tooltips.hpp"
#include "unit_display.hpp"
#include "util.hpp"
#include "SDL_image.h"
@ -292,8 +293,17 @@ void display::scroll(double xmove, double ymove)
}
}
void display::zoom(double amount)
int display::hex_size() const
{
return int(zoom_);
}
double display::zoom(double amount)
{
if(amount == 0.0) {
return zoom_/DefaultZoom;
}
const double orig_xpos = xpos_;
const double orig_ypos = ypos_;
@ -308,7 +318,7 @@ void display::zoom(double amount)
zoom_ = orig_zoom;
xpos_ = orig_xpos;
ypos_ = orig_ypos;
return;
return zoom_/DefaultZoom;
}
xpos_ *= zoom_;
@ -325,7 +335,7 @@ void display::zoom(double amount)
xpos_ = orig_xpos;
ypos_ = orig_ypos;
zoom_ = orig_zoom;
return;
return zoom_/DefaultZoom;
}
energy_bar_rect_.x = -1;
@ -333,6 +343,8 @@ void display::zoom(double amount)
image::set_zoom(zoom_);
map_labels_.recalculate_labels();
invalidate_all();
return zoom_/DefaultZoom;
}
void display::default_zoom()
@ -1082,7 +1094,6 @@ void display::draw_unit_on_tile(int x, int y, SDL_Surface* unit_image_override,
const int max_energy = 80;
double energy_size = 1.0;
bool face_left = true;
if(unit_image_override != NULL)
sdl_add_ref(unit_image_override);
@ -1172,7 +1183,17 @@ void display::draw_unit_on_tile(int x, int y, SDL_Surface* unit_image_override,
energy_size = double(it->second.max_hitpoints())/double(max_energy);
}
face_left = it->second.facing_left();
if(it->second.facing_left() == false) {
//reverse the image here. image::reverse_image is more efficient, however
//it can be used only if we are sure that unit_image came from image::get_image.
//Since we aren't sure of that in the case of overrides, use the less efficient
//flip_surface if the image has been over-ridden.
if(unit_image_override == NULL) {
unit_image.assign(image::reverse_image(unit_image));
} else {
unit_image.assign(flip_surface(unit_image));
}
}
}
if(deadUnit_ == gamemap::location(x,y)) {
@ -1217,7 +1238,7 @@ void display::draw_unit_on_tile(int x, int y, SDL_Surface* unit_image_override,
ellipse_front.assign(image::get_image(buf));
}
draw_unit(xpos,ypos - height_adjust,unit_image,face_left,false,
draw_unit(xpos,ypos - height_adjust,unit_image,false,
highlight_ratio,blend_with,submerge,ellipse_back,ellipse_front);
}
@ -1417,8 +1438,7 @@ void display::draw_tile(int x, int y, SDL_Surface* unit_image, double alpha, Uin
if(game_config::debug && debugHighlights_.count(gamemap::location(x,y))) {
const scoped_sdl_surface cross(image::get_image(game_config::cross_image));
if(cross != NULL)
draw_unit(xpos,ypos,cross,false,false,
debugHighlights_[loc],0);
draw_unit(xpos,ypos,cross,false,debugHighlights_[loc],0);
}
}
@ -1475,7 +1495,7 @@ void display::draw_footstep(const gamemap::location& loc, int xloc, int yloc)
}
}
const scoped_sdl_surface image(image::get_image(*image_str));
scoped_sdl_surface image(image::get_image(*image_str));
if(image == NULL) {
std::cerr << "Could not find image: " << *image_str << "\n";
return;
@ -1486,7 +1506,11 @@ void display::draw_footstep(const gamemap::location& loc, int xloc, int yloc)
const bool vflip = (direction >= gamemap::location::SOUTH_EAST &&
direction <= gamemap::location::SOUTH_WEST);
draw_unit(xloc,yloc,image,hflip,vflip,0.5);
if(!hflip) {
image.assign(image::reverse_image(image));
}
draw_unit(xloc,yloc,image,vflip,0.5);
if(show_time && route_.move_left > 0 && route_.move_left < 10) {
//draw number in yellow if terrain is light, else draw in black
@ -1703,7 +1727,7 @@ SDL_Surface* display::getFlag(gamemap::TERRAIN terrain, int x, int y)
const gamemap::location loc(x,y);
for(size_t i = 0; i != teams_.size(); ++i) {
if(teams_[i].owns_tower(loc) && (!fogged(x,y) || !shrouded(x,y) && !teams_[currentTeam_].is_enemy(i+1))) {
if(teams_[i].owns_village(loc) && (!fogged(x,y) || !shrouded(x,y) && !teams_[currentTeam_].is_enemy(i+1))) {
char buf[50];
sprintf(buf,"terrain/flag-team%d.png",i+1);
return image::get_image(buf);
@ -1716,62 +1740,14 @@ SDL_Surface* display::getFlag(gamemap::TERRAIN terrain, int x, int y)
void display::blit_surface(int x, int y, SDL_Surface* surface, SDL_Rect* srcrect, SDL_Rect* clip_rect)
{
SDL_Surface* const target = video().getSurface();
SDL_Rect clip;
if(clip_rect == NULL) {
clip = screen_area();
clip_rect = &clip;
SDL_Rect dst = {x,y,0,0};
if(clip_rect != NULL) {
const clip_rect_setter(target,*clip_rect);
SDL_BlitSurface(surface,srcrect,target,&dst);
} else {
SDL_BlitSurface(surface,srcrect,target,&dst);
}
SDL_Rect src;
if(srcrect == NULL) {
src.x = 0;
src.y = 0;
src.w = surface->w;
src.h = surface->h;
srcrect = &src;
}
if(x + srcrect->w < clip_rect->x) {
return;
}
if(y + srcrect->h < clip_rect->y) {
return;
}
if(x > clip_rect->x + clip_rect->w) {
return;
}
if(y > clip_rect->y + clip_rect->h) {
return;
}
if(x < clip_rect->x) {
const int diff = clip_rect->x - x;
srcrect->x += diff;
srcrect->w -= diff;
x = clip_rect->x;
}
if(y < clip_rect->y) {
const int diff = clip_rect->y - y;
srcrect->y += diff;
srcrect->h -= diff;
y = clip_rect->y;
}
if(x + srcrect->w > clip_rect->x + clip_rect->w) {
srcrect->w = clip_rect->x + clip_rect->w - x;
}
if(y + srcrect->h > clip_rect->y + clip_rect->h) {
srcrect->h = clip_rect->y + clip_rect->h - y;
}
SDL_Rect dstrect = {x,y,0,0};
SDL_BlitSurface(surface,srcrect,target,&dstrect);
}
SDL_Surface* display::getMinimap(int w, int h)
@ -1814,28 +1790,11 @@ void display::set_route(const paths::route* route)
invalidate_route();
}
void display::move_unit(const std::vector<gamemap::location>& path, unit& u)
void display::remove_footstep(const gamemap::location& loc)
{
for(size_t i = 0; i+1 < path.size(); ++i) {
if(path[i+1].x > path[i].x) {
u.set_facing_left(true);
} else if(path[i+1].x < path[i].x) {
u.set_facing_left(false);
}
//remove the footstep we are on.
const std::vector<gamemap::location>::iterator it = std::find(route_.steps.begin(),route_.steps.end(),path[i]);
if(it != route_.steps.end())
route_.steps.erase(it);
move_unit_between(path[i],path[i+1],u);
}
//make sure the entire path is cleaned properly
for(std::vector<gamemap::location>::const_iterator it = path.begin();
it != path.end(); ++it) {
draw_tile(it->x,it->y);
}
const std::vector<gamemap::location>::iterator it = std::find(route_.steps.begin(),route_.steps.end(),loc);
if(it != route_.steps.end())
route_.steps.erase(it);
}
void display::float_label(const gamemap::location& loc, const std::string& text,
@ -1850,545 +1809,12 @@ void display::float_label(const gamemap::location& loc, const std::string& text,
0,-2,60,screen_area());
}
bool display::unit_attack_ranged(const gamemap::location& a,
const gamemap::location& b, int damage,
const attack_type& attack)
{
const bool hide = update_locked() || fogged(a.x,a.y) && fogged(b.x,b.y)
|| preferences::show_combat() == false;
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);
//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 real_last_missile = last_missile - first_missile;
const int missile_impact = -first_missile;
const int time_resolution = 20;
const int acceleration = 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::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 end_at = maximum((damage+1)*time_resolution+missile_impact,
maximum(attack.get_last_frame(),real_last_missile));
const double xsrc = get_location_x(a);
const double ysrc = get_location_y(a);
const double xdst = get_location_x(b);
const double ydst = get_location_y(b);
gamemap::location update_tiles[6];
get_adjacent_tiles(a,update_tiles);
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;
bool dead = false;
const int drain_speed = 1*acceleration;
int flash_num = 0;
int ticks = SDL_GetTicks();
bool shown_label = false;
for(int i = begin_at; i < end_at; i += time_resolution*acceleration) {
events::pump();
//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) {
const std::string& sfx = hits ? sfx_it->on_hit : sfx_it->on_miss;
if(sfx.empty() == false) {
sound::play_sound(hits ? sfx_it->on_hit : sfx_it->on_miss);
}
++sfx_it;
}
if(!hide && hits && !played_hit_sound && i >= play_hit_sound_at) {
sound::play_sound(hit_sound);
played_hit_sound = true;
}
const std::string* unit_image = attack.get_frame(i);
if(unit_image == NULL) {
unit_image =
&att->second.type().image_fighting(attack_type::LONG_RANGE);
}
if(!hide) {
const scoped_sdl_surface image((unit_image == NULL) ? NULL : image::get_image(*unit_image));
draw_tile(a.x,a.y,image);
}
if(damage > 0 && i >= missile_impact && shown_label == false) {
shown_label = true;
char buf[50];
sprintf(buf,"%d",damage);
float_label(b,buf,255,0,0);
}
Uint32 defensive_colour = 0;
double defensive_alpha = 1.0;
if(damage > 0 && i >= missile_impact) {
if(def->second.gets_hit(minimum<int>(drain_speed,damage))) {
dead = true;
damage = 0;
} else {
damage -= drain_speed;
}
if(flash_num == 0 || flash_num == 2) {
defensive_alpha = 0.0;
defensive_colour = rgb(200,0,0);
}
++flash_num;
}
for(int j = 0; j != 6; ++j) {
if(update_tiles[j] != b) {
draw_tile(update_tiles[j].x,update_tiles[j].y);
}
}
draw_tile(b.x,b.y,NULL,defensive_alpha,defensive_colour);
if(i >= 0 && i < real_last_missile && !hide) {
const int missile_frame = i + first_missile;
const std::string* missile_image
= attack.get_frame(missile_frame,NULL,
attack_type::MISSILE_FRAME,dir);
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)
missile_image = &default_missile;
else
missile_image = &default_diag_missile;
}
const scoped_sdl_surface img(image::get_image(*missile_image));
if(img != NULL) {
double pos = double(missile_impact - i)/double(missile_impact);
if(pos < 0.0)
pos = 0.0;
const int xpos = int(xsrc*pos + xdst*(1.0-pos));
const int ypos = int(ysrc*pos + ydst*(1.0-pos));
draw_unit(xpos,ypos,img,!hflip,vflip);
}
}
const int wait_time = ticks + time_resolution - SDL_GetTicks();
if(wait_time > 0 && !hide)
SDL_Delay(wait_time);
ticks = SDL_GetTicks();
update_display();
}
def->second.set_defending(false);
draw_tile(a.x,a.y);
draw_tile(b.x,b.y);
if(dead) {
unit_die(b);
}
return dead;
}
bool display::unit_attack(const gamemap::location& a,
const gamemap::location& b, int damage,
const attack_type& attack)
{
const bool hide = update_locked() || fogged(a.x,a.y) && fogged(b.x,b.y)
|| preferences::show_combat() == false;
if(!hide) {
const double side_threshhold = 80.0;
double xloc = get_location_x(a);
double yloc = get_location_y(a);
//we try to scroll the map if the unit is at the edge.
//keep track of the old position, and if the map moves at all,
//then recenter it on the unit
double oldxpos = xpos_;
double oldypos = ypos_;
if(xloc < map_area().x + side_threshhold) {
scroll(xloc - side_threshhold - map_area().x,0.0);
}
if(yloc < map_area().y + side_threshhold) {
scroll(0.0,yloc - side_threshhold - map_area().y);
}
if(xloc + zoom_ > map_area().x + map_area().w - side_threshhold) {
scroll(((xloc + zoom_) -
(map_area().x + map_area().w - side_threshhold)),0.0);
}
if(yloc + zoom_ > map_area().y + map_area().h - side_threshhold) {
scroll(0.0,((yloc + zoom_) -
(map_area().y + map_area().h - side_threshhold)));
}
if(oldxpos != xpos_ || oldypos != ypos_) {
scroll_to_tile(a.x,a.y,WARP);
}
}
log_scope("unit_attack");
invalidate_all();
draw(true,true);
const unit_map::iterator att = units_.find(a);
assert(att != units_.end());
unit& attacker = att->second;
const unit_map::iterator def = units_.find(b);
assert(def != units_.end());
if(b.x > a.x) {
att->second.set_facing_left(true);
def->second.set_facing_left(false);
} else if(b.x < a.x) {
att->second.set_facing_left(false);
def->second.set_facing_left(true);
}
if(attack.range() == attack_type::LONG_RANGE) {
return unit_attack_ranged(a,b,damage,attack);
}
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::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 int time_resolution = 20;
const int acceleration = turbo() ? 5 : 1;
def->second.set_defending(true,attack_type::SHORT_RANGE);
const int begin_at = minimum<int>(-200,attack.get_first_frame());
const int end_at = maximum<int>((damage+1)*time_resolution,
maximum<int>(200,
attack.get_last_frame()));
const double xsrc = get_location_x(a);
const double ysrc = get_location_y(a);
const double xdst = get_location_x(b)*0.6 + xsrc*0.4;
const double ydst = get_location_y(b)*0.6 + ysrc*0.4;
gamemap::location update_tiles[6];
get_adjacent_tiles(b,update_tiles);
bool dead = false;
const int drain_speed = 1*acceleration;
int flash_num = 0;
int ticks = SDL_GetTicks();
hiddenUnit_ = a;
const gamemap::TERRAIN src_terrain = map_.get_terrain(a);
const gamemap::TERRAIN dst_terrain = map_.get_terrain(b);
const double src_height_adjust = attacker.is_flying() ? 0 : map_.get_terrain_info(src_terrain).unit_height_adjust() * (zoom_/DefaultZoom);
const double dst_height_adjust = attacker.is_flying() ? 0 : map_.get_terrain_info(dst_terrain).unit_height_adjust() * (zoom_/DefaultZoom);
const double src_submerge = attacker.is_flying() ? 0 : map_.get_terrain_info(src_terrain).unit_submerge();
const double dst_submerge = attacker.is_flying() ? 0 : map_.get_terrain_info(dst_terrain).unit_submerge();
bool shown_label = false;
for(int i = begin_at; i < end_at; i += time_resolution*acceleration) {
events::pump();
//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) {
const std::string& sfx = hits ? sfx_it->on_hit : sfx_it->on_miss;
if(sfx.empty() == false) {
sound::play_sound(hits ? sfx_it->on_hit : sfx_it->on_miss);
}
++sfx_it;
}
if(!hide && hits && !played_hit_sound && i >= play_hit_sound_at) {
sound::play_sound(hit_sound);
played_hit_sound = true;
}
for(int j = 0; j != 6; ++j) {
draw_tile(update_tiles[j].x,update_tiles[j].y);
}
Uint32 defender_colour = 0;
double defender_alpha = 1.0;
if(damage > 0 && i >= 0 && shown_label == false) {
shown_label = true;
char buf[50];
sprintf(buf,"%d",damage);
float_label(b,buf,255,0,0);
}
if(damage > 0 && i >= 0) {
if(def->second.gets_hit(minimum<int>(drain_speed,damage))) {
dead = true;
damage = 0;
} else {
damage -= drain_speed;
}
if(flash_num == 0 || flash_num == 2) {
defender_alpha = 0.0;
defender_colour = rgb(200,0,0);
}
++flash_num;
}
draw_tile(b.x,b.y,NULL,defender_alpha,defender_colour);
int xoffset = 0;
const std::string* unit_image = attack.get_frame(i,&xoffset);
if(!attacker.facing_left())
xoffset *= -1;
xoffset = int(double(xoffset)*(zoom_/DefaultZoom));
if(unit_image == NULL)
unit_image = &attacker.image();
const scoped_sdl_surface image((unit_image == NULL) ? NULL : image::get_image(*unit_image));
const double pos = double(i)/double(i < 0 ? begin_at : end_at);
const int posx = int(pos*xsrc + (1.0-pos)*xdst) + xoffset;
const int posy = int(pos*ysrc + (1.0-pos)*ydst);
const int height_adjust = int(src_height_adjust*pos + dst_height_adjust*(1.0-pos));
const double submerge = src_submerge*pos + dst_submerge*(1.0-pos);
if(image != NULL && !hide)
draw_unit(posx,posy-height_adjust,image,attacker.facing_left(),false,1.0,0,submerge);
const int wait_time = ticks + time_resolution - SDL_GetTicks();
if(wait_time > 0 && !hide)
SDL_Delay(wait_time);
ticks = SDL_GetTicks();
update_display();
}
hiddenUnit_ = gamemap::location();
def->second.set_defending(false);
draw_tile(a.x,a.y);
draw_tile(b.x,b.y);
if(dead) {
unit_die(b);
}
return dead;
}
void display::unit_die(const gamemap::location& loc, SDL_Surface* image)
{
if(update_locked() || fogged(loc.x,loc.y)
|| preferences::show_combat() == false)
return;
const unit_map::const_iterator u = units_.find(loc);
assert(u != units_.end());
const std::string& die_sound = u->second.type().die_sound();
if(die_sound != "" && die_sound != "null") {
sound::play_sound(die_sound);
}
const int frame_time = 30;
int ticks = SDL_GetTicks();
for(double alpha = 1.0; alpha > 0.0; alpha -= 0.05) {
draw_tile(loc.x,loc.y,image,alpha);
const int wait_time = ticks + frame_time - SDL_GetTicks();
if(wait_time > 0 && !turbo())
SDL_Delay(wait_time);
ticks = SDL_GetTicks();
update_display();
}
draw(true,true);
}
void display::move_unit_between(const gamemap::location& a,
const gamemap::location& b,
const unit& u)
{
if(update_locked() || team_valid()
&& teams_[currentTeam_].fogged(a.x,a.y)
&& teams_[currentTeam_].fogged(b.x,b.y))
return;
const bool face_left = u.facing_left();
const double side_threshhold = 80.0;
double xsrc = get_location_x(a);
double ysrc = get_location_y(a);
double xdst = get_location_x(b);
double ydst = get_location_y(b);
const gamemap::TERRAIN src_terrain = map_.get_terrain(a);
const gamemap::TERRAIN dst_terrain = map_.get_terrain(b);
const int src_height_adjust = u.is_flying() ? 0 : int(map_.get_terrain_info(src_terrain).unit_height_adjust() * (zoom_/DefaultZoom));
const int dst_height_adjust = u.is_flying() ? 0 : int(map_.get_terrain_info(dst_terrain).unit_height_adjust() * (zoom_/DefaultZoom));
const double src_submerge = u.is_flying() ? 0 : int(map_.get_terrain_info(src_terrain).unit_submerge());
const double dst_submerge = u.is_flying() ? 0 : int(map_.get_terrain_info(dst_terrain).unit_submerge());
const double nsteps = turbo() ? 3.0 : 10.0;
const double xstep = (xdst - xsrc)/nsteps;
const double ystep = (ydst - ysrc)/nsteps;
const int time_between_frames = turbo() ? 2 : 10;
int ticks = SDL_GetTicks();
int skips = 0;
for(double i = 0.0; i < nsteps; i += 1.0) {
events::pump();
const scoped_sdl_surface image(image::get_image(u.type().image()));
if(image == NULL) {
std::cerr << "failed to get image " << u.type().image() << "\n";
return;
}
xsrc = get_location_x(a);
ysrc = get_location_y(a);
xdst = get_location_x(b);
ydst = get_location_y(b);
double xloc = xsrc + xstep*i;
double yloc = ysrc + ystep*i;
//we try to scroll the map if the unit is at the edge.
//keep track of the old position, and if the map moves at all,
//then recenter it on the unit
double oldxpos = xpos_;
double oldypos = ypos_;
if(xloc < side_threshhold) {
scroll(xloc - side_threshhold,0.0);
}
if(yloc < side_threshhold) {
scroll(0.0,yloc - side_threshhold);
}
if(xloc + double(image->w) > this->mapx() - side_threshhold) {
scroll(((xloc + double(image->w)) -
(this->mapx() - side_threshhold)),0.0);
}
if(yloc + double(image->h) > this->y() - side_threshhold) {
scroll(0.0,((yloc + double(image->h)) -
(this->y() - side_threshhold)));
}
if(oldxpos != xpos_ || oldypos != ypos_) {
scroll_to_tile(b.x,b.y,WARP);
}
xsrc = get_location_x(a);
ysrc = get_location_y(a);
xdst = get_location_x(b);
ydst = get_location_y(b);
xloc = xsrc + xstep*i;
yloc = ysrc + ystep*i;
//invalidate the source tile and all adjacent tiles,
//since the unit can partially overlap adjacent tiles
gamemap::location adjacent[6];
get_adjacent_tiles(a,adjacent);
draw_tile(a.x,a.y);
for(int tile = 0; tile != 6; ++tile) {
draw_tile(adjacent[tile].x,adjacent[tile].y);
}
const int height_adjust = src_height_adjust + (dst_height_adjust-src_height_adjust)*(i/nsteps);
const double submerge = src_submerge + (dst_submerge-src_submerge)*(i/nsteps);
draw(false);
draw_unit((int)xloc,(int)yloc - height_adjust,image,face_left,false,1.0,0,submerge);
const int new_ticks = SDL_GetTicks();
const int wait_time = time_between_frames - (new_ticks - ticks);
if(wait_time > 0) {
SDL_Delay(wait_time);
}
ticks = SDL_GetTicks();
if(wait_time >= 0 || skips == 4 || (i+1.0) >= nsteps) {
skips = 0;
update_display();
} else {
++skips;
}
}
}
void display::draw_unit(int x, int y, SDL_Surface* image,
bool reverse, bool upside_down,
double alpha, Uint32 blendto, double submerged,
bool upside_down, double alpha, Uint32 blendto, double submerged,
SDL_Surface* ellipse_back, SDL_Surface* ellipse_front)
{
if(ellipse_back != NULL) {
draw_unit(x,y,ellipse_back,false,false,blendto == 0 ? alpha : 1.0,0,submerged);
draw_unit(x,y,ellipse_back,false,blendto == 0 ? alpha : 1.0,0,submerged);
}
sdl_add_ref(image);
@ -2398,10 +1824,6 @@ void display::draw_unit(int x, int y, SDL_Surface* image,
surf.assign(flop_surface(surf));
}
if(!reverse) {
surf.assign(flip_surface(surf));
}
if(alpha > 1.0) {
surf.assign(brighten_image(surf,alpha));
} else if(alpha != 1.0 && blendto != 0) {
@ -2432,7 +1854,7 @@ void display::draw_unit(int x, int y, SDL_Surface* image,
}
if(ellipse_front != NULL) {
draw_unit(x,y,ellipse_front,false,false,blendto == 0 ? alpha : 1.0,0,submerged);
draw_unit(x,y,ellipse_front,false,blendto == 0 ? alpha : 1.0,0,submerged);
}
}

View file

@ -79,12 +79,16 @@ public:
void scroll(double xmov, double ymov);
//function which zooms the display by the specified amount. Negative
//valeus zoom out.
void zoom(double amount);
//valeus zoom out. Returns the current zoom ratio
double zoom(double amount=0.0);
//function to take the zoom amount to the default.
void default_zoom();
//function which returns the size of a hex in pixels
//(from top tip to bottom tip or left edge to right edge)
int hex_size() const;
enum SCROLL_TYPE { SCROLL, WARP };
//function which will scroll such that location x,y is on-screen.
@ -152,13 +156,8 @@ public:
double get_location_x(const gamemap::location& loc) const;
double get_location_y(const gamemap::location& loc) const;
//function to display movement of a unit along the given sequence of tiles
void move_unit(const std::vector<gamemap::location>& path, unit& u);
//function to show one unit taking one attack attempt at another. damage
//is the amount of damage inflicted, and 0 if the attack misses.
bool unit_attack(const gamemap::location& a, const gamemap::location& b,
int damage, const attack_type& attack);
//function to remove a footstep from a specific location
void remove_footstep(const gamemap::location& loc);
//function to draw the tile at location (x,y). If unit_image is not NULL,
//then it will be used, otherwise the unit's default image will be used.
@ -284,8 +283,6 @@ public:
const theme::menu* menu_pressed(int mousex, int mousey, bool button_pressed);
void unit_die(const gamemap::location& loc, SDL_Surface* image=NULL);
void add_observer(const std::string& name);
void remove_observer(const std::string& name);
@ -295,14 +292,6 @@ public:
enum MESSAGE_TYPE { MESSAGE_PUBLIC, MESSAGE_PRIVATE };
void add_chat_message(const std::string& speaker, int side, const std::string& msg, MESSAGE_TYPE type);
private:
display(const display&);
void operator=(const display&);
void move_unit_between(const gamemap::location& a,
const gamemap::location& b,
const unit& u);
//function to draw the image of a unit at a certain location
//x,y: pixel location on screen to draw the unit
//image: the image of the unit
@ -314,13 +303,12 @@ private:
//submerged: the amount of the unit out of 1.0 that is submerged
// (presumably under water) and thus shouldn't be drawn
void draw_unit(int x, int y, SDL_Surface* image,
bool reverse, bool upside_down=false,
double alpha=1.0, Uint32 blendto=0, double submerged=0.0,
bool upside_down=false,double alpha=1.0, Uint32 blendto=0, double submerged=0.0,
SDL_Surface* ellipse_back=NULL, SDL_Surface* ellipse_front=NULL);
bool unit_attack_ranged(const gamemap::location& a,
const gamemap::location& b,
int damage, const attack_type& attack);
private:
display(const display&);
void operator=(const display&);
void draw_sidebar();
void draw_minimap(int x, int y, int w, int h);

View file

@ -17,7 +17,7 @@
namespace game_config
{
int base_income = 2;
int tower_income = 1;
int village_income = 1;
int heal_amount = 4;
int healer_heals_per_turn = 8;
int cure_amount = 8;
@ -64,7 +64,7 @@ namespace game_config
const config& v = *cfg;
base_income = atoi(v["base_income"].c_str());
tower_income = atoi(v["village_income"].c_str());
village_income = atoi(v["village_income"].c_str());
heal_amount = atoi(v["heal_amount"].c_str());
healer_heals_per_turn = atoi(v["healer_heals_per_turn"].c_str());
cure_amount = atoi(v["cure_amount"].c_str());

View file

@ -21,7 +21,7 @@
namespace game_config
{
extern int base_income;
extern int tower_income;
extern int village_income;
extern int heal_amount;
extern int healer_heals_per_turn;
extern int cure_amount;

View file

@ -19,6 +19,7 @@
#include "replay.hpp"
#include "show_dialog.hpp"
#include "sound.hpp"
#include "unit_display.hpp"
#include "util.hpp"
#include <cstdlib>
@ -434,7 +435,7 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
atoi(yvals[i].c_str())-1));
}
screen->move_unit(path,dummy_unit);
unit_display::move_unit(*screen,*game_map,path,dummy_unit);
}
}
@ -868,7 +869,7 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
if(game_events::unit_matches_filter(un,cfg)) {
if(cfg["animate"] == "yes") {
screen->scroll_to_tile(un->first.x,un->first.y,display::WARP);
screen->unit_die(un->first);
unit_display::unit_die(*screen,un->first,un->second);
}
units->erase(un++);

View file

@ -20,6 +20,8 @@ mini_terrain_cache_map mini_terrain_cache;
typedef std::map<std::string,SDL_Surface*> image_map;
image_map images_,scaledImages_,unmaskedImages_,greyedImages_,brightenedImages_;
std::map<SDL_Surface*,SDL_Surface*> reversedImages_;
int red_adjust = 0, green_adjust = 0, blue_adjust = 0;
std::string image_mask;
@ -74,6 +76,7 @@ void flush_cache()
clear_surfaces(greyedImages_);
clear_surfaces(brightenedImages_);
clear_surfaces(mini_terrain_cache);
clear_surfaces(reversedImages_);
}
}
@ -111,6 +114,7 @@ void set_colour_adjustment(int r, int g, int b)
clear_surfaces(scaledImages_);
clear_surfaces(greyedImages_);
clear_surfaces(brightenedImages_);
clear_surfaces(reversedImages_);
}
}
@ -136,6 +140,7 @@ void set_image_mask(const std::string& image)
clear_surfaces(scaledImages_);
clear_surfaces(greyedImages_);
clear_surfaces(brightenedImages_);
clear_surfaces(reversedImages_);
}
}
@ -148,6 +153,7 @@ void set_zoom(double amount)
clear_surfaces(greyedImages_);
clear_surfaces(brightenedImages_);
clear_surfaces(unmaskedImages_);
clear_surfaces(reversedImages_);
}
}
@ -286,6 +292,28 @@ SDL_Surface* get_image_dim(const std::string& filename, size_t x, size_t y)
return surf;
}
SDL_Surface* reverse_image(SDL_Surface* surf)
{
if(surf == NULL) {
return NULL;
}
const std::map<SDL_Surface*,SDL_Surface*>::iterator itor = reversedImages_.find(surf);
if(itor != reversedImages_.end()) {
sdl_add_ref(itor->second);
return itor->second;
}
SDL_Surface* const rev = flip_surface(surf);
if(rev == NULL) {
return NULL;
}
reversedImages_.insert(std::pair<SDL_Surface*,SDL_Surface*>(surf,rev));
sdl_add_ref(rev);
return rev;
}
void register_image(const std::string& id, SDL_Surface* surf)
{
if(surf == NULL) {

View file

@ -9,78 +9,84 @@
class team;
//this module manages the cache of images. With an image name, you can get
//the surface corresponding to that image, and don't need to free the image.
//Note that surfaces returned from here are invalidated whenever events::pump()
//is called, and so shouldn't be kept, but should be regotten from here as
//needed.
///this module manages the cache of images. With an image name, you can get
///the surface corresponding to that image, and don't need to free the image.
///Note that surfaces returned from here are invalidated whenever events::pump()
///is called, and so shouldn't be kept, but should be regotten from here as
///needed.
//
//images come in a number of varieties:
// - unscaled: no modifications have been done on the image.
// - scaled: images are scaled to the size of a tile
// - greyed: images are scaled and in greyscale
// - brightened: images are scaled and brighter than normal.
///images come in a number of varieties:
/// - unscaled: no modifications have been done on the image.
/// - scaled: images are scaled to the size of a tile
/// - unmasked: images are scaled, but have no time of day masking applied to them
/// - greyed: images are scaled and in greyscale
/// - brightened: images are scaled and brighter than normal.
namespace image {
//the image manager is responsible for setting up images, and destroying
//all images when the program exits. It should probably
//be created once for the life of the program
///the image manager is responsible for setting up images, and destroying
///all images when the program exits. It should probably
///be created once for the life of the program
struct manager
{
manager();
~manager();
};
//function to set the program's icon to the window manager.
//must be called after SDL_Init() is called, but before setting the
//video mode
///function to set the program's icon to the window manager.
///must be called after SDL_Init() is called, but before setting the
///video mode
void set_wm_icon();
//will make all scaled images have these rgb values added to all
//their pixels. i.e. add a certain colour hint to images. useful
//for representing day/night. Invalidates all scaled images.
///will make all scaled images have these rgb values added to all
///their pixels. i.e. add a certain colour hint to images. useful
///for representing day/night. Invalidates all scaled images.
void set_colour_adjustment(int r, int g, int b);
//function to get back the current colour adjustment values
///function to get back the current colour adjustment values
void get_colour_adjustment(int *r, int *g, int *b);
//function which sets a certain image as a 'mask' for all scaled images.
//the 'mask' is blitted onto all scaled images.
///function which sets a certain image as a 'mask' for all scaled images.
///the 'mask' is blitted onto all scaled images.
void set_image_mask(const std::string& image_name);
//sets the pixel format used by the images. Is called every time the
//video mode changes. Invalidates all images.
///sets the pixel format used by the images. Is called every time the
///video mode changes. Invalidates all images.
void set_pixel_format(SDL_PixelFormat* format);
//sets the amount scaled images should be scaled. Invalidates all
//scaled images.
///sets the amount scaled images should be scaled. Invalidates all
///scaled images.
void set_zoom(double zoom);
enum TYPE { UNSCALED, SCALED, UNMASKED, GREYED, BRIGHTENED };
enum COLOUR_ADJUSTMENT { ADJUST_COLOUR, NO_ADJUST_COLOUR };
//function to get the surface corresponding to an image.
//note that this surface must be freed by the user by calling
//SDL_FreeSurface
///function to get the surface corresponding to an image.
///note that this surface must be freed by the user by calling
///SDL_FreeSurface()
SDL_Surface* get_image(const std::string& filename,TYPE type=SCALED, COLOUR_ADJUSTMENT adj=ADJUST_COLOUR);
//function to get a scaled image, but scale it to specific dimensions.
//if you later try to get the same image using get_image() the image will
//have the dimensions specified here.
//Note that this surface must be freed by the user by calling SDL_FreeSurface
///function to get a scaled image, but scale it to specific dimensions.
///if you later try to get the same image using get_image() the image will
///have the dimensions specified here.
///Note that this surface must be freed by the user by calling SDL_FreeSurface
SDL_Surface* get_image_dim(const std::string& filename, size_t x, size_t y);
//function to register an image with the given id. Calls to get_image(id,UNSCALED) will
//return this image. register_image() will take ownership of this image and free
//it when the cache is cleared (change of video mode or colour adjustment).
//If there is already an image registered with this id, that image will be freed
//and replaced with this image.
///function to reverse an image. The image MUST have originally been returned from
///an image:: function. Returned images have the same semantics as for get_image()
///and must be freed using SDL_FreeSurface()
SDL_Surface* reverse_image(SDL_Surface* surf);
///function to register an image with the given id. Calls to get_image(id,UNSCALED) will
///return this image. register_image() will take ownership of this image and free
///it when the cache is cleared (change of video mode or colour adjustment).
///If there is already an image registered with this id, that image will be freed
///and replaced with this image.
void register_image(const std::string& id, SDL_Surface* surf);
//the surface returned must be freed by the user
SDL_Surface* getMinimap(int w, int h, const gamemap& map_, int lawful_bonus,
const team* tm=NULL);
///function to create the minimap for a given map
///the surface returned must be freed by the user
SDL_Surface* getMinimap(int w, int h, const gamemap& map_, int lawful_bonus, const team* tm=NULL);
}
#endif

View file

@ -189,7 +189,7 @@ gamemap::gamemap(const config& cfg, const std::string& data) : tiles_(1)
}
if(is_village(c)) {
towers_.push_back(location(int(x),int(y)));
villages_.push_back(location(int(x),int(y)));
}
if(x >= tiles_.size()) {

View file

@ -33,7 +33,7 @@ public:
//in dynamically because they're special. It's asserted that there will
//be corresponding entries for these types of terrain in the terrain
//configuration file.
enum { FOGGED = '~', VOID_TERRAIN = ' ', KEEP = 'K', CASTLE = 'C', TOWER = 't', FOREST = 'f' };
enum { FOGGED = '~', VOID_TERRAIN = ' ', KEEP = 'K', CASTLE = 'C', VILLAGE = 't', FOREST = 'f' };
//the name of the terrain is the terrain itself, the underlying terrain
//is the name of the terrain for game-logic purposes. I.e. if the terrain
@ -125,7 +125,7 @@ public:
}
//function to return a list of the locations of villages on the map
const std::vector<location>& towers() const { return towers_; }
const std::vector<location>& villages() const { return villages_; }
//function to get the corresponding terrain_type information object
//for a given type of terrain
@ -147,7 +147,7 @@ private:
std::map<std::string,terrain_type> terrain_;
std::vector<std::vector<TERRAIN> > tiles_;
std::vector<location> towers_;
std::vector<location> villages_;
location startingPositions_[10];
mutable std::map<location,TERRAIN> borderCache_;

View file

@ -157,16 +157,16 @@ void find_routes(const gamemap& map, const gamestatus& status,
get_adjacent_tiles(loc,&locs[0]);
//check for teleporting units -- we must be on a vacant (or occupied by this unit)
//tower, that is controlled by our team to be able to teleport.
//village, that is controlled by our team to be able to teleport.
if(allow_teleport && map.is_village(loc) &&
current_team.owns_tower(loc) && (starting_pos || units.count(loc) == 0)) {
const std::vector<gamemap::location>& towers = map.towers();
current_team.owns_village(loc) && (starting_pos || units.count(loc) == 0)) {
const std::vector<gamemap::location>& villages = map.villages();
//if we are on a tower, see all friendly towers that we can
//if we are on a village, see all friendly villages that we can
//teleport to
for(std::vector<gamemap::location>::const_iterator t = towers.begin();
t != towers.end(); ++t) {
if(!current_team.owns_tower(*t) || units.count(*t))
for(std::vector<gamemap::location>::const_iterator t = villages.begin();
t != villages.end(); ++t) {
if(!current_team.owns_village(*t) || units.count(*t))
continue;
locs.push_back(*t);

View file

@ -377,7 +377,7 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
//if the expense is less than the number of villages owned,
//then we don't have to pay anything at all
const int expense = team_upkeep(units,player_number) -
team_it->towers().size();
team_it->villages().size();
if(expense > 0) {
team_it->spend_gold(expense);
}
@ -573,7 +573,7 @@ redo_turn:
}
const int remaining_gold = teams[0].gold();
const int finishing_bonus_per_turn = map.towers().size()*game_config::tower_income + game_config::base_income;
const int finishing_bonus_per_turn = map.villages().size()*game_config::village_income + game_config::base_income;
const int turns_left = maximum<int>(0,status.number_of_turns() - status.turn());
const int finishing_bonus = end_level.gold_bonus ?
(finishing_bonus_per_turn * turns_left) : 0;

View file

@ -28,6 +28,7 @@
#include "statistics.hpp"
#include "tooltips.hpp"
#include "unit.hpp"
#include "unit_display.hpp"
#include "util.hpp"
#include <cmath>
@ -106,9 +107,9 @@ void play_turn(game_data& gameinfo, game_state& state_of_game,
std::set<gamemap::location> allowed_teleports;
if(u.type().teleports()) {
allowed_teleports = vacant_towers(current_team.towers(),units);
allowed_teleports = vacant_villages(current_team.villages(),units);
teleports = &allowed_teleports;
if(current_team.towers().count(ui->first))
if(current_team.villages().count(ui->first))
allowed_teleports.insert(ui->first);
}
@ -373,9 +374,9 @@ void turn_info::mouse_motion(const SDL_MouseMotionEvent& event)
std::set<gamemap::location> allowed_teleports;
if(can_teleport) {
allowed_teleports = vacant_towers(current_team.towers(),units_);
allowed_teleports = vacant_villages(current_team.villages(),units_);
teleports = &allowed_teleports;
if(current_team.towers().count(un->first))
if(current_team.villages().count(un->first))
allowed_teleports.insert(un->first);
}
@ -805,9 +806,9 @@ void turn_info::left_click(const SDL_MouseButtonEvent& event)
std::set<gamemap::location> allowed_teleports;
if(u.type().teleports()) {
allowed_teleports = vacant_towers(current_team.towers(),units_);
allowed_teleports = vacant_villages(current_team.villages(),units_);
teleports = &allowed_teleports;
if(current_team.towers().count(it->first))
if(current_team.villages().count(it->first))
allowed_teleports.insert(it->first);
}
@ -1142,7 +1143,7 @@ void turn_info::undo()
}
if(map_.is_village(route.front())) {
get_tower(route.front(),teams_,action.original_village_owner,units_);
get_village(route.front(),teams_,action.original_village_owner,units_);
}
action.starting_moves = u->second.movement_left();
@ -1150,7 +1151,7 @@ void turn_info::undo()
unit un = u->second;
un.set_goto(gamemap::location());
units_.erase(u);
gui_.move_unit(route,un);
unit_display::move_unit(gui_,map_,route,un);
un.set_movement(starting_moves);
units_.insert(std::pair<gamemap::location,unit>(route.back(),un));
gui_.draw_tile(route.back().x,route.back().y);
@ -1220,12 +1221,12 @@ void turn_info::redo()
unit un = u->second;
un.set_goto(gamemap::location());
units_.erase(u);
gui_.move_unit(route,un);
unit_display::move_unit(gui_,map_,route,un);
un.set_movement(starting_moves);
units_.insert(std::pair<gamemap::location,unit>(route.back(),un));
if(map_.is_village(route.back())) {
get_tower(route.back(),teams_,un.side()-1,units_);
get_village(route.back(),teams_,un.side()-1,units_);
}
gui_.draw_tile(route.back().x,route.back().y);

View file

@ -25,6 +25,7 @@
#include "show_dialog.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "unit_display.hpp"
#include "util.hpp"
#include <cstdio>
@ -707,16 +708,17 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
rt->second.steps.push_back(dst);
if(!replayer.skipping())
disp.move_unit(rt->second.steps,current_unit);
if(!replayer.skipping()) {
unit_display::move_unit(disp,map,rt->second.steps,current_unit);
}
current_unit.set_movement(rt->second.move_left);
u = units.insert(std::pair<gamemap::location,unit>(dst,current_unit)).first;
if(map.is_village(dst)) {
const int orig_owner = tower_owner(dst,teams) + 1;
const int orig_owner = village_owner(dst,teams) + 1;
if(orig_owner != team_num) {
u->second.set_movement(0);
get_tower(dst,teams,team_num-1,units);
get_village(dst,teams,team_num-1,units);
}
}

View file

@ -57,7 +57,7 @@ team::team_info::team_info(const config& cfg)
const std::string& village_income = cfg["village_gold"];
if(village_income.empty())
income_per_village = game_config::tower_income;
income_per_village = game_config::village_income;
else
income_per_village = atoi(village_income.c_str());
@ -226,7 +226,7 @@ team::team(const config& cfg, int gold) : gold_(gold), info_(cfg)
//load in the villages the side controls at the start
const config::child_list& villages = cfg.get_children("village");
for(config::child_list::const_iterator v = villages.begin(); v != villages.end(); ++v) {
towers_.insert(gamemap::location(**v));
villages_.insert(gamemap::location(**v));
}
const std::string& shroud_data = cfg["shroud_data"];
@ -252,7 +252,7 @@ void team::write(config& cfg) const
cfg["gold"] = buf;
//write village locations
for(std::set<gamemap::location>::const_iterator t = towers_.begin(); t != towers_.end(); ++t) {
for(std::set<gamemap::location>::const_iterator t = villages_.begin(); t != villages_.end(); ++t) {
t->write(cfg.add_child("village"));
}
@ -270,31 +270,31 @@ void team::write(config& cfg) const
cfg["shroud_data"] = shroud_str.str();
}
void team::get_tower(const gamemap::location& loc)
void team::get_village(const gamemap::location& loc)
{
towers_.insert(loc);
villages_.insert(loc);
}
void team::lose_tower(const gamemap::location& loc)
void team::lose_village(const gamemap::location& loc)
{
if(owns_tower(loc)) {
towers_.erase(towers_.find(loc));
if(owns_village(loc)) {
villages_.erase(villages_.find(loc));
}
}
void team::clear_towers()
void team::clear_villages()
{
towers_.clear();
villages_.clear();
}
const std::set<gamemap::location>& team::towers() const
const std::set<gamemap::location>& team::villages() const
{
return towers_;
return villages_;
}
bool team::owns_tower(const gamemap::location& loc) const
bool team::owns_village(const gamemap::location& loc) const
{
return towers_.count(loc) > 0;
return villages_.count(loc) > 0;
}
int team::gold() const
@ -305,7 +305,7 @@ int team::gold() const
int team::income() const
{
return atoi(info_.income.c_str()) +
towers_.size()*info_.income_per_village+game_config::base_income;
villages_.size()*info_.income_per_village+game_config::base_income;
}
void team::new_turn()
@ -561,11 +561,11 @@ int team::nteams()
}
}
const std::set<gamemap::location> vacant_towers(const std::set<gamemap::location>& towers, const unit_map& units)
const std::set<gamemap::location> vacant_villages(const std::set<gamemap::location>& villages, const unit_map& units)
{
std::set<gamemap::location> res;
for(std::set<gamemap::location>::const_iterator i = towers.begin(); i != towers.end(); ++i) {
for(std::set<gamemap::location>::const_iterator i = villages.begin(); i != villages.end(); ++i) {
if(units.count(*i) == 0) {
res.insert(*i);
}

View file

@ -68,11 +68,11 @@ public:
void write(config& cfg) const;
void get_tower(const gamemap::location&);
void lose_tower(const gamemap::location&);
void clear_towers();
const std::set<gamemap::location>& towers() const;
bool owns_tower(const gamemap::location&) const;
void get_village(const gamemap::location&);
void lose_village(const gamemap::location&);
void clear_villages();
const std::set<gamemap::location>& villages() const;
bool owns_village(const gamemap::location&) const;
int gold() const;
int income() const;
@ -126,7 +126,7 @@ public:
private:
int gold_;
std::set<gamemap::location> towers_;
std::set<gamemap::location> villages_;
typedef std::vector<std::vector<bool> > shroud_map;
shroud_map shroud_, fog_;

View file

@ -405,6 +405,8 @@ bool unit::matches_filter(const config& cfg) const
const std::string& side = cfg["side"];
const std::string& weapon = cfg["has_weapon"];
const std::string& role = cfg["role"];
const std::string& race = cfg["race"];
const std::string& gender = cfg["gender"];
if(description.empty() == false && description != this->underlying_description()) {
return false;
@ -436,8 +438,20 @@ bool unit::matches_filter(const config& cfg) const
}
}
if(ability.empty() == false && this->type().has_ability(ability) == false)
if(ability.empty() == false && this->type().has_ability(ability) == false) {
return false;
}
if(race.empty() == false && this->type().race() != race) {
return false;
}
if(gender.empty() == false) {
const unit_race::GENDER gender_type = gender == "female" ? unit_race::FEMALE : unit_race::MALE;
if(gender_type != this->type().gender()) {
return false;
}
}
if(side.empty() == false && this->side() != atoi(side.c_str()))
{
@ -1070,7 +1084,7 @@ team_data calculate_team_data(const team& tm, int side, const unit_map& units)
team_data res;
res.units = team_units(units,side);
res.upkeep = team_upkeep(units,side);
res.villages = tm.towers().size();
res.villages = tm.villages().size();
res.expenses = maximum<int>(0,res.upkeep - res.villages);
res.net_income = tm.income() - res.expenses;
res.gold = tm.gold();

View file

@ -235,7 +235,7 @@ team_data calculate_team_data(const class team& tm, int side, const unit_map& un
std::string get_team_name(int side, const unit_map& units);
const std::set<gamemap::location> vacant_towers(const std::set<gamemap::location>& towers, const unit_map& units);
const std::set<gamemap::location> vacant_villages(const std::set<gamemap::location>& villages, const unit_map& units);
//this object is used to temporary place a unit in the unit map, swapping out any unit
//that is already there. On destruction, it restores the unit map to its original state.

571
src/unit_display.cpp Normal file
View file

@ -0,0 +1,571 @@
#include "events.hpp"
#include "game_config.hpp"
#include "image.hpp"
#include "log.hpp"
#include "preferences.hpp"
#include "sound.hpp"
#include "unit_display.hpp"
#include "util.hpp"
namespace
{
void move_unit_between(display& disp, const gamemap& map, const gamemap::location& a, const gamemap::location& b, const unit& u)
{
if(disp.update_locked() || disp.fogged(a.x,a.y) && disp.fogged(b.x,b.y)) {
return;
}
const bool face_left = u.facing_left();
const double side_threshhold = 80.0;
double xsrc = disp.get_location_x(a);
double ysrc = disp.get_location_y(a);
double xdst = disp.get_location_x(b);
double ydst = disp.get_location_y(b);
const gamemap::TERRAIN src_terrain = map.get_terrain(a);
const gamemap::TERRAIN dst_terrain = map.get_terrain(b);
const int src_height_adjust = u.is_flying() ? 0 : int(map.get_terrain_info(src_terrain).unit_height_adjust() * disp.zoom());
const int dst_height_adjust = u.is_flying() ? 0 : int(map.get_terrain_info(dst_terrain).unit_height_adjust() * disp.zoom());
const double src_submerge = u.is_flying() ? 0 : int(map.get_terrain_info(src_terrain).unit_submerge());
const double dst_submerge = u.is_flying() ? 0 : int(map.get_terrain_info(dst_terrain).unit_submerge());
const double nsteps = disp.turbo() ? 3.0 : 10.0;
const double xstep = (xdst - xsrc)/nsteps;
const double ystep = (ydst - ysrc)/nsteps;
const int time_between_frames = disp.turbo() ? 2 : 10;
int ticks = SDL_GetTicks();
int skips = 0;
for(double i = 0.0; i < nsteps; i += 1.0) {
events::pump();
scoped_sdl_surface image(image::get_image(u.type().image()));
if(!face_left) {
image.assign(image::reverse_image(image));
}
if(image == NULL) {
std::cerr << "failed to get image " << u.type().image() << "\n";
return;
}
xsrc = disp.get_location_x(a);
ysrc = disp.get_location_y(a);
xdst = disp.get_location_x(b);
ydst = disp.get_location_y(b);
double xloc = xsrc + xstep*i;
double yloc = ysrc + ystep*i;
//we try to scroll the map if the unit is at the edge.
//keep track of the old position, and if the map moves at all,
//then recenter it on the unit
if(xloc < side_threshhold) {
disp.scroll(xloc - side_threshhold,0.0);
}
if(yloc < side_threshhold) {
disp.scroll(0.0,yloc - side_threshhold);
}
if(xloc + double(image->w) > disp.mapx() - side_threshhold) {
disp.scroll(((xloc + double(image->w)) -
(disp.mapx() - side_threshhold)),0.0);
}
if(yloc + double(image->h) > disp.y() - side_threshhold) {
disp.scroll(0.0,((yloc + double(image->h)) -
(disp.y() - side_threshhold)));
}
if(xsrc != disp.get_location_x(a) || ysrc != disp.get_location_y(a)) {
disp.scroll_to_tile(b.x,b.y,display::WARP);
xsrc = disp.get_location_x(a);
ysrc = disp.get_location_y(a);
xdst = disp.get_location_x(b);
ydst = disp.get_location_y(b);
xloc = xsrc + xstep*i;
yloc = ysrc + ystep*i;
}
//invalidate the source tile and all adjacent tiles,
//since the unit can partially overlap adjacent tiles
gamemap::location adjacent[6];
get_adjacent_tiles(a,adjacent);
disp.draw_tile(a.x,a.y);
for(int tile = 0; tile != 6; ++tile) {
disp.draw_tile(adjacent[tile].x,adjacent[tile].y);
}
const int height_adjust = src_height_adjust + (dst_height_adjust-src_height_adjust)*(i/nsteps);
const double submerge = src_submerge + (dst_submerge-src_submerge)*(i/nsteps);
disp.draw(false);
disp.draw_unit((int)xloc,(int)yloc - height_adjust,image,false,1.0,0,submerge);
const int new_ticks = SDL_GetTicks();
const int wait_time = time_between_frames - (new_ticks - ticks);
if(wait_time > 0) {
SDL_Delay(wait_time);
}
ticks = SDL_GetTicks();
if(wait_time >= 0 || skips == 4 || (i+1.0) >= nsteps) {
skips = 0;
disp.update_display();
} else {
++skips;
}
}
}
}
namespace unit_display
{
void move_unit(display& disp, const gamemap& map, const std::vector<gamemap::location>& path, unit& u)
{
for(size_t i = 0; i+1 < path.size(); ++i) {
if(path[i+1].x > path[i].x) {
u.set_facing_left(true);
} else if(path[i+1].x < path[i].x) {
u.set_facing_left(false);
}
disp.remove_footstep(path[i]);
move_unit_between(disp,map,path[i],path[i+1],u);
}
//make sure the entire path is cleaned properly
for(std::vector<gamemap::location>::const_iterator it = path.begin(); it != path.end(); ++it) {
disp.draw_tile(it->x,it->y);
}
}
void unit_die(display& disp, const gamemap::location& loc, const unit& u)
{
if(disp.update_locked() || disp.fogged(loc.x,loc.y) || preferences::show_combat() == false) {
return;
}
const std::string& die_sound = u.type().die_sound();
if(die_sound != "" && die_sound != "null") {
sound::play_sound(die_sound);
}
const int frame_time = 30;
int ticks = SDL_GetTicks();
for(double alpha = 1.0; alpha > 0.0; alpha -= 0.05) {
disp.draw_tile(loc.x,loc.y,NULL,alpha);
const int wait_time = ticks + frame_time - SDL_GetTicks();
if(wait_time > 0 && !disp.turbo())
SDL_Delay(wait_time);
ticks = SDL_GetTicks();
disp.update_display();
}
disp.draw(true,true);
}
namespace {
bool unit_attack_ranged(display& disp, unit_map& units, const gamemap& map,
const gamemap::location& a, const gamemap::location& b, int damage,
const attack_type& attack)
{
const bool hide = disp.update_locked() || disp.fogged(a.x,a.y) && disp.fogged(b.x,b.y)
|| preferences::show_combat() == false;
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);
//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 real_last_missile = last_missile - first_missile;
const int missile_impact = -first_missile;
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::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 end_at = maximum((damage+1)*time_resolution+missile_impact,
maximum(attack.get_last_frame(),real_last_missile));
const double xsrc = disp.get_location_x(a);
const double ysrc = disp.get_location_y(a);
const double xdst = disp.get_location_x(b);
const double ydst = disp.get_location_y(b);
gamemap::location update_tiles[6];
get_adjacent_tiles(a,update_tiles);
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;
bool dead = false;
const int drain_speed = 1*acceleration;
int flash_num = 0;
int ticks = SDL_GetTicks();
bool shown_label = false;
for(int i = begin_at; i < end_at; i += time_resolution*acceleration) {
events::pump();
//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) {
const std::string& sfx = hits ? sfx_it->on_hit : sfx_it->on_miss;
if(sfx.empty() == false) {
sound::play_sound(hits ? sfx_it->on_hit : sfx_it->on_miss);
}
++sfx_it;
}
if(!hide && hits && !played_hit_sound && i >= play_hit_sound_at) {
sound::play_sound(hit_sound);
played_hit_sound = true;
}
const std::string* unit_image = attack.get_frame(i);
if(unit_image == NULL) {
unit_image = &att->second.type().image_fighting(attack_type::LONG_RANGE);
}
if(!hide) {
const scoped_sdl_surface image((unit_image == NULL) ? NULL : image::get_image(*unit_image));
disp.draw_tile(a.x,a.y,image);
}
if(damage > 0 && i >= missile_impact && shown_label == false) {
shown_label = true;
disp.float_label(b,lexical_cast<std::string>(damage),255,0,0);
}
Uint32 defensive_colour = 0;
double defensive_alpha = 1.0;
if(damage > 0 && i >= missile_impact) {
if(def->second.gets_hit(minimum<int>(drain_speed,damage))) {
dead = true;
damage = 0;
} else {
damage -= drain_speed;
}
if(flash_num == 0 || flash_num == 2) {
defensive_alpha = 0.0;
defensive_colour = disp.rgb(200,0,0);
}
++flash_num;
}
for(int j = 0; j != 6; ++j) {
if(update_tiles[j] != b) {
disp.draw_tile(update_tiles[j].x,update_tiles[j].y);
}
}
disp.draw_tile(b.x,b.y,NULL,defensive_alpha,defensive_colour);
if(i >= 0 && i < real_last_missile && !hide) {
const int missile_frame = i + first_missile;
const std::string* missile_image = attack.get_frame(missile_frame,NULL,
attack_type::MISSILE_FRAME,dir);
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)
missile_image = &default_missile;
else
missile_image = &default_diag_missile;
}
scoped_sdl_surface img(image::get_image(*missile_image));
if(hflip) {
img.assign(image::reverse_image(img));
}
if(img != NULL) {
double pos = double(missile_impact - i)/double(missile_impact);
if(pos < 0.0) {
pos = 0.0;
}
const int xpos = int(xsrc*pos + xdst*(1.0-pos));
const int ypos = int(ysrc*pos + ydst*(1.0-pos));
disp.draw_unit(xpos,ypos,img,vflip);
}
}
const int wait_time = ticks + time_resolution - SDL_GetTicks();
if(wait_time > 0 && !hide) {
SDL_Delay(wait_time);
}
ticks = SDL_GetTicks();
disp.update_display();
}
def->second.set_defending(false);
disp.draw_tile(a.x,a.y);
disp.draw_tile(b.x,b.y);
if(dead) {
unit_die(disp,def->first,def->second);
}
return dead;
}
} //end anon namespace
bool unit_attack(display& disp, unit_map& units, const gamemap& map,
const gamemap::location& a, const gamemap::location& b, int damage,
const attack_type& attack)
{
const bool hide = disp.update_locked() || disp.fogged(a.x,a.y) && disp.fogged(b.x,b.y)
|| preferences::show_combat() == false;
if(!hide) {
const double side_threshhold = 80.0;
double xloc = disp.get_location_x(a);
double yloc = disp.get_location_y(a);
SDL_Rect area = disp.map_area();
//we try to scroll the map if the unit is at the edge.
//keep track of the old position, and if the map moves at all,
//then recenter it on the unit
if(xloc < area.x + side_threshhold) {
disp.scroll(xloc - side_threshhold - disp.map_area().x,0.0);
}
if(yloc < area.y + side_threshhold) {
disp.scroll(0.0,yloc - side_threshhold - area.y);
}
if(xloc + disp.hex_size() > area.x + area.w - side_threshhold) {
disp.scroll(((xloc + disp.hex_size()) - (area.x + area.w - side_threshhold)),0.0);
}
if(yloc + disp.hex_size() > area.y + area.h - side_threshhold) {
disp.scroll(0.0,((yloc + disp.hex_size()) - (area.y + area.h - side_threshhold)));
}
if(xloc != disp.get_location_x(a) || yloc != disp.get_location_y(a)) {
disp.scroll_to_tile(a.x,a.y,display::WARP);
}
}
log_scope("unit_attack");
disp.invalidate_all();
disp.draw(true,true);
const unit_map::iterator att = units.find(a);
assert(att != units.end());
unit& attacker = att->second;
const unit_map::iterator def = units.find(b);
assert(def != units.end());
if(b.x > a.x) {
att->second.set_facing_left(true);
def->second.set_facing_left(false);
} else if(b.x < a.x) {
att->second.set_facing_left(false);
def->second.set_facing_left(true);
}
if(attack.range() == attack_type::LONG_RANGE) {
return unit_attack_ranged(disp,units,map,a,b,damage,attack);
}
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::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 int time_resolution = 20;
const int acceleration = disp.turbo() ? 5 : 1;
def->second.set_defending(true,attack_type::SHORT_RANGE);
const int begin_at = minimum<int>(-200,attack.get_first_frame());
const int end_at = maximum<int>((damage+1)*time_resolution,
maximum<int>(200,attack.get_last_frame()));
const double xsrc = disp.get_location_x(a);
const double ysrc = disp.get_location_y(a);
const double xdst = disp.get_location_x(b)*0.6 + xsrc*0.4;
const double ydst = disp.get_location_y(b)*0.6 + ysrc*0.4;
gamemap::location update_tiles[6];
get_adjacent_tiles(b,update_tiles);
bool dead = false;
const int drain_speed = 1*acceleration;
int flash_num = 0;
int ticks = SDL_GetTicks();
disp.hide_unit(a);
const gamemap::TERRAIN src_terrain = map.get_terrain(a);
const gamemap::TERRAIN dst_terrain = map.get_terrain(b);
const double src_height_adjust = attacker.is_flying() ? 0 : map.get_terrain_info(src_terrain).unit_height_adjust() * disp.zoom();
const double dst_height_adjust = attacker.is_flying() ? 0 : map.get_terrain_info(dst_terrain).unit_height_adjust() * disp.zoom();
const double src_submerge = attacker.is_flying() ? 0 : map.get_terrain_info(src_terrain).unit_submerge();
const double dst_submerge = attacker.is_flying() ? 0 : map.get_terrain_info(dst_terrain).unit_submerge();
bool shown_label = false;
for(int i = begin_at; i < end_at; i += time_resolution*acceleration) {
events::pump();
//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) {
const std::string& sfx = hits ? sfx_it->on_hit : sfx_it->on_miss;
if(sfx.empty() == false) {
sound::play_sound(hits ? sfx_it->on_hit : sfx_it->on_miss);
}
++sfx_it;
}
if(!hide && hits && !played_hit_sound && i >= play_hit_sound_at) {
sound::play_sound(hit_sound);
played_hit_sound = true;
}
for(int j = 0; j != 6; ++j) {
disp.draw_tile(update_tiles[j].x,update_tiles[j].y);
}
Uint32 defender_colour = 0;
double defender_alpha = 1.0;
if(damage > 0 && i >= 0 && shown_label == false) {
shown_label = true;
disp.float_label(b,lexical_cast<std::string>(damage),255,0,0);
}
if(damage > 0 && i >= 0) {
if(def->second.gets_hit(minimum<int>(drain_speed,damage))) {
dead = true;
damage = 0;
} else {
damage -= drain_speed;
}
if(flash_num == 0 || flash_num == 2) {
defender_alpha = 0.0;
defender_colour = disp.rgb(200,0,0);
}
++flash_num;
}
disp.draw_tile(b.x,b.y,NULL,defender_alpha,defender_colour);
int xoffset = 0;
const std::string* unit_image = attack.get_frame(i,&xoffset);
if(!attacker.facing_left())
xoffset *= -1;
xoffset = int(double(xoffset)*disp.zoom());
if(unit_image == NULL) {
unit_image = &attacker.image();
}
scoped_sdl_surface image((unit_image == NULL) ? NULL : image::get_image(*unit_image));
if(attacker.facing_left() == false) {
image.assign(image::reverse_image(image));
}
const double pos = double(i)/double(i < 0 ? begin_at : end_at);
const int posx = int(pos*xsrc + (1.0-pos)*xdst) + xoffset;
const int posy = int(pos*ysrc + (1.0-pos)*ydst);
const int height_adjust = int(src_height_adjust*pos + dst_height_adjust*(1.0-pos));
const double submerge = src_submerge*pos + dst_submerge*(1.0-pos);
if(image != NULL && !hide) {
disp.draw_unit(posx,posy-height_adjust,image,false,1.0,0,submerge);
}
const int wait_time = ticks + time_resolution - SDL_GetTicks();
if(wait_time > 0 && !hide) {
SDL_Delay(wait_time);
}
ticks = SDL_GetTicks();
disp.update_display();
}
disp.hide_unit(gamemap::location());
def->second.set_defending(false);
disp.draw_tile(a.x,a.y);
disp.draw_tile(b.x,b.y);
if(dead) {
unit_display::unit_die(disp,def->first,def->second);
}
return dead;
}
}

30
src/unit_display.hpp Normal file
View file

@ -0,0 +1,30 @@
#ifndef UNIT_DISPLAY_HPP_INCLUDED
#define UNIT_DISPLAY_HPP_INCLUDED
#include "display.hpp"
#include "unit.hpp"
///the unit_display namespace contains a number of free functions
///which display units performing various on-screen actions - moving,
///attacking, and dying
namespace unit_display
{
///a function to display a unit moving along a given path
void move_unit(display& disp, const gamemap& map, const std::vector<gamemap::location>& path, unit& u);
///a function to show a unit fading out. Note that this only shows the effect, it doesn't
///actually kill the unit.
void unit_die(display& disp, const gamemap::location& loc, const unit& u);
///a function to make the unit on tile 'a' attack the unit on tile 'b'.
///the 'damage' will be subtracted from the unit's hitpoints, and a die effect will be
///displayed if the unit dies.
///true is returned if the defending unit is dead, and should be removed from the
///playing field.
bool unit_attack(display& disp, unit_map& units, const gamemap& map,
const gamemap::location& a, const gamemap::location& b, int damage,
const attack_type& attack);
}
#endif

View file

@ -748,6 +748,16 @@ const std::vector<config*>& unit_type::possible_traits() const
unit_race::GENDER unit_type::gender() const { return gender_; }
const std::string& unit_type::race() const
{
if(race_ == NULL) {
static const std::string empty_string;
return empty_string;
}
return race_->name();
}
game_data::game_data(const config& cfg)
{
static const std::vector<config*> dummy_traits;

View file

@ -208,6 +208,8 @@ public:
unit_race::GENDER gender() const;
const std::string& race() const;
private:
const config& cfg_;