Lift some bounds-checking code into display.cpp.

This commit is contained in:
Eric S. Raymond 2007-06-22 12:32:43 +00:00
parent 1960c1e1a7
commit 928ee74075
2 changed files with 226 additions and 224 deletions

View file

@ -63,7 +63,9 @@ namespace {
size_t sunset_timer = 0;
}
map_display::map_display(CVideo& video, const gamemap& map, const config& theme_cfg) : screen_(video), map_(map), theme_(theme_cfg,screen_area()), zoom_(DefaultZoom)
map_display::map_display(CVideo& video, const gamemap& map, const config& theme_cfg) :
screen_(video), map_(map), xpos_(0), ypos_(0),
theme_(theme_cfg,screen_area()), zoom_(DefaultZoom)
{
if(non_interactive()) {
screen_.lock_updates(true);
@ -103,7 +105,7 @@ const SDL_Rect& map_display::map_area() const
return res;
}
bool display::outside_area(const SDL_Rect& area, const int x, const int y) const
bool map_display::outside_area(const SDL_Rect& area, const int x, const int y) const
{
const int x_thresh = hex_width();
const int y_thresh = hex_size();
@ -111,6 +113,195 @@ bool display::outside_area(const SDL_Rect& area, const int x, const int y) const
y < area.y || y > area.y + area.h - y_thresh);
}
// This function use the screen as reference
const gamemap::location map_display::hex_clicked_on(int xclick, int yclick,
gamemap::location::DIRECTION* nearest_hex,
gamemap::location::DIRECTION* second_nearest_hex) const
{
const SDL_Rect& rect = map_area();
if(point_in_rect(xclick,yclick,rect) == false) {
return gamemap::location();
}
xclick -= rect.x;
yclick -= rect.y;
return pixel_position_to_hex(xpos_ + xclick, ypos_ + yclick, nearest_hex, second_nearest_hex);
}
// This function use the rect of map_area as reference
const gamemap::location map_display::pixel_position_to_hex(int x, int y,
gamemap::location::DIRECTION* nearest_hex,
gamemap::location::DIRECTION* second_nearest_hex) const
{
// adjust for the 1 hex border
x -= hex_width() ;
y -= hex_size();
const int s = hex_size();
const int tesselation_x_size = hex_width() * 2;
const int tesselation_y_size = s;
const int x_base = x / tesselation_x_size * 2;
const int x_mod = x % tesselation_x_size;
const int y_base = y / tesselation_y_size;
const int y_mod = y % tesselation_y_size;
int x_modifier = 0;
int y_modifier = 0;
if (y_mod < tesselation_y_size / 2) {
if ((x_mod * 2 + y_mod) < (s / 2)) {
x_modifier = -1;
y_modifier = -1;
} else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
x_modifier = 0;
y_modifier = 0;
} else {
x_modifier = 1;
y_modifier = -1;
}
} else {
if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
x_modifier = -1;
y_modifier = 0;
} else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
x_modifier = 0;
y_modifier = 0;
} else {
x_modifier = 1;
y_modifier = 0;
}
}
const gamemap::location res(x_base + x_modifier, y_base + y_modifier);
if(nearest_hex != NULL) {
// our x and y use the map_area as reference
// and the coorfinates given by get_location use the screen as reference
// so we need to convert it
const int centerx = (get_location_x(res) - map_area().x + xpos_) + hex_size()/2 - hex_width();
const int centery = (get_location_y(res) - map_area().y + ypos_) + hex_size()/2 - hex_size();
const int x_offset = x - centerx;
const int y_offset = y - centery;
if(y_offset > 0) {
if(x_offset > y_offset/2) {
*nearest_hex = gamemap::location::SOUTH_EAST;
if(second_nearest_hex != NULL) {
if(x_offset/2 > y_offset) {
*second_nearest_hex = gamemap::location::NORTH_EAST;
} else {
*second_nearest_hex = gamemap::location::SOUTH;
}
}
} else if(-x_offset > y_offset/2) {
*nearest_hex = gamemap::location::SOUTH_WEST;
if(second_nearest_hex != NULL) {
if(-x_offset/2 > y_offset) {
*second_nearest_hex = gamemap::location::NORTH_WEST;
} else {
*second_nearest_hex = gamemap::location::SOUTH;
}
}
} else {
*nearest_hex = gamemap::location::SOUTH;
if(second_nearest_hex != NULL) {
if(x_offset > 0) {
*second_nearest_hex = gamemap::location::SOUTH_EAST;
} else {
*second_nearest_hex = gamemap::location::SOUTH_WEST;
}
}
}
} else { // y_offset <= 0
if(x_offset > -y_offset/2) {
*nearest_hex = gamemap::location::NORTH_EAST;
if(second_nearest_hex != NULL) {
if(x_offset/2 > -y_offset) {
*second_nearest_hex = gamemap::location::SOUTH_EAST;
} else {
*second_nearest_hex = gamemap::location::NORTH;
}
}
} else if(-x_offset > -y_offset/2) {
*nearest_hex = gamemap::location::NORTH_WEST;
if(second_nearest_hex != NULL) {
if(-x_offset/2 > -y_offset) {
*second_nearest_hex = gamemap::location::SOUTH_WEST;
} else {
*second_nearest_hex = gamemap::location::NORTH;
}
}
} else {
*nearest_hex = gamemap::location::NORTH;
if(second_nearest_hex != NULL) {
if(x_offset > 0) {
*second_nearest_hex = gamemap::location::NORTH_EAST;
} else {
*second_nearest_hex = gamemap::location::NORTH_WEST;
}
}
}
}
}
return res;
}
void map_display::get_rect_hex_bounds(SDL_Rect rect, gamemap::location &topleft, gamemap::location &bottomright) const
{
// change the coordinates of the rect send to be relative
// to the map area instead of the screen area
const SDL_Rect& map_rect = map_area();
rect.x -= map_rect.x;
rect.y -= map_rect.y;
rect.w += map_rect.x;
rect.h += map_rect.y;
const int tile_width = hex_width();
// adjust for the 1 hex border
topleft.x = -1 + (xpos_ + rect.x) / tile_width;
topleft.y = -1 + (ypos_ + rect.y - (is_odd(topleft.x) ? zoom_/2 : 0)) / zoom_;
bottomright.x = -1 + (xpos_ + rect.x + rect.w) / tile_width;
bottomright.y = -1 + ((ypos_ + rect.y + rect.h) - (is_odd(bottomright.x) ? zoom_/2 : 0)) / zoom_;
// This routine does a rough approximation so might be off by one
// to be sure enough tiles are incuded the boundries are increased
// by one if the terrain is "on the map" due to the extra border
// this uses a bit larger area.
// FIXME this routine should properly determine what to update and
// not increase by one just to be sure.
if(topleft.x >= -1) {
topleft.x--;
}
if(topleft.y >= -1) {
topleft.y--;
}
if(bottomright.x <= map_.x()) {
bottomright.x++;
}
if(bottomright.y <= map_.y()) {
bottomright.y++;
}
}
gamemap::location map_display::minimap_location_on(int x, int y)
{
const SDL_Rect rect = minimap_area();
if(x < rect.x || y < rect.y ||
x >= rect.x + rect.w || y >= rect.y + rect.h) {
return gamemap::location();
}
const double xdiv = double(rect.w) / double(map_.x());
const double ydiv = double(rect.h) / double(map_.y());
return gamemap::location(int((x - rect.x)/xdiv),int((y-rect.y)/ydiv));
}
void map_display::screenshot()
{
std::string datadir = get_screenshot_dir();
@ -143,7 +334,6 @@ display::display(unit_map& units, CVideo& video, const gamemap& map,
const config& theme_cfg, const config& cfg, const config& level) :
map_display(video, map, theme_cfg),
_scroll_event("scrolled"),
xpos_(0), ypos_(0),
units_(units),
temp_unit_(NULL),
minimap_(NULL), redrawMinimap_(false), redraw_background_(true),
@ -298,195 +488,6 @@ void display::highlight_hex(gamemap::location hex)
}
}
// This function use the screen as reference
const gamemap::location display::hex_clicked_on(int xclick, int yclick,
gamemap::location::DIRECTION* nearest_hex,
gamemap::location::DIRECTION* second_nearest_hex) const
{
const SDL_Rect& rect = map_area();
if(point_in_rect(xclick,yclick,rect) == false) {
return gamemap::location();
}
xclick -= rect.x;
yclick -= rect.y;
return pixel_position_to_hex(xpos_ + xclick, ypos_ + yclick, nearest_hex, second_nearest_hex);
}
// This function use the rect of map_area as reference
const gamemap::location display::pixel_position_to_hex(int x, int y,
gamemap::location::DIRECTION* nearest_hex,
gamemap::location::DIRECTION* second_nearest_hex) const
{
// adjust for the 1 hex border
x -= hex_width() ;
y -= hex_size();
const int s = hex_size();
const int tesselation_x_size = hex_width() * 2;
const int tesselation_y_size = s;
const int x_base = x / tesselation_x_size * 2;
const int x_mod = x % tesselation_x_size;
const int y_base = y / tesselation_y_size;
const int y_mod = y % tesselation_y_size;
int x_modifier = 0;
int y_modifier = 0;
if (y_mod < tesselation_y_size / 2) {
if ((x_mod * 2 + y_mod) < (s / 2)) {
x_modifier = -1;
y_modifier = -1;
} else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
x_modifier = 0;
y_modifier = 0;
} else {
x_modifier = 1;
y_modifier = -1;
}
} else {
if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
x_modifier = -1;
y_modifier = 0;
} else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
x_modifier = 0;
y_modifier = 0;
} else {
x_modifier = 1;
y_modifier = 0;
}
}
const gamemap::location res(x_base + x_modifier, y_base + y_modifier);
if(nearest_hex != NULL) {
// our x and y use the map_area as reference
// and the coorfinates given by get_location use the screen as reference
// so we need to convert it
const int centerx = (get_location_x(res) - map_area().x + xpos_) + hex_size()/2 - hex_width();
const int centery = (get_location_y(res) - map_area().y + ypos_) + hex_size()/2 - hex_size();
const int x_offset = x - centerx;
const int y_offset = y - centery;
if(y_offset > 0) {
if(x_offset > y_offset/2) {
*nearest_hex = gamemap::location::SOUTH_EAST;
if(second_nearest_hex != NULL) {
if(x_offset/2 > y_offset) {
*second_nearest_hex = gamemap::location::NORTH_EAST;
} else {
*second_nearest_hex = gamemap::location::SOUTH;
}
}
} else if(-x_offset > y_offset/2) {
*nearest_hex = gamemap::location::SOUTH_WEST;
if(second_nearest_hex != NULL) {
if(-x_offset/2 > y_offset) {
*second_nearest_hex = gamemap::location::NORTH_WEST;
} else {
*second_nearest_hex = gamemap::location::SOUTH;
}
}
} else {
*nearest_hex = gamemap::location::SOUTH;
if(second_nearest_hex != NULL) {
if(x_offset > 0) {
*second_nearest_hex = gamemap::location::SOUTH_EAST;
} else {
*second_nearest_hex = gamemap::location::SOUTH_WEST;
}
}
}
} else { // y_offset <= 0
if(x_offset > -y_offset/2) {
*nearest_hex = gamemap::location::NORTH_EAST;
if(second_nearest_hex != NULL) {
if(x_offset/2 > -y_offset) {
*second_nearest_hex = gamemap::location::SOUTH_EAST;
} else {
*second_nearest_hex = gamemap::location::NORTH;
}
}
} else if(-x_offset > -y_offset/2) {
*nearest_hex = gamemap::location::NORTH_WEST;
if(second_nearest_hex != NULL) {
if(-x_offset/2 > -y_offset) {
*second_nearest_hex = gamemap::location::SOUTH_WEST;
} else {
*second_nearest_hex = gamemap::location::NORTH;
}
}
} else {
*nearest_hex = gamemap::location::NORTH;
if(second_nearest_hex != NULL) {
if(x_offset > 0) {
*second_nearest_hex = gamemap::location::NORTH_EAST;
} else {
*second_nearest_hex = gamemap::location::NORTH_WEST;
}
}
}
}
}
return res;
}
void display::get_rect_hex_bounds(SDL_Rect rect, gamemap::location &topleft, gamemap::location &bottomright) const
{
// change the coordinates of the rect send to be relative
// to the map area instead of the screen area
const SDL_Rect& map_rect = map_area();
rect.x -= map_rect.x;
rect.y -= map_rect.y;
rect.w += map_rect.x;
rect.h += map_rect.y;
const int tile_width = hex_width();
// adjust for the 1 hex border
topleft.x = -1 + (xpos_ + rect.x) / tile_width;
topleft.y = -1 + (ypos_ + rect.y - (is_odd(topleft.x) ? zoom_/2 : 0)) / zoom_;
bottomright.x = -1 + (xpos_ + rect.x + rect.w) / tile_width;
bottomright.y = -1 + ((ypos_ + rect.y + rect.h) - (is_odd(bottomright.x) ? zoom_/2 : 0)) / zoom_;
// This routine does a rough approximation so might be off by one
// to be sure enough tiles are incuded the boundries are increased
// by one if the terrain is "on the map" due to the extra border
// this uses a bit larger area.
// FIXME this routine should properly determine what to update and
// not increase by one just to be sure.
if(topleft.x >= -1) {
topleft.x--;
}
if(topleft.y >= -1) {
topleft.y--;
}
if(bottomright.x <= map_.x()) {
bottomright.x++;
}
if(bottomright.y <= map_.y()) {
bottomright.y++;
}
}
gamemap::location display::minimap_location_on(int x, int y)
{
const SDL_Rect rect = minimap_area();
if(x < rect.x || y < rect.y ||
x >= rect.x + rect.w || y >= rect.y + rect.h) {
return gamemap::location();
}
const double xdiv = double(rect.w) / double(map_.x());
const double ydiv = double(rect.h) / double(map_.y());
return gamemap::location(int((x - rect.x)/xdiv),int((y-rect.y)/ydiv));
}
void display::scroll(int xmove, int ymove)
{
const int orig_x = xpos_;

View file

@ -86,6 +86,9 @@ public:
const SDL_Rect& map_outside_area() const
{ return theme_.main_map_location(screen_area()); }
//check if pixel x,y is outside specified area
bool outside_area(const SDL_Rect& area, const int x,const int y) const;
//function which returns the width of a pixel, up to where the
//next hex starts (i.e. not entirely from tip to tip -- use
//hex_size() to get the distance from tip to tip)
@ -97,12 +100,42 @@ public:
// Returns the current zoom factor.
double get_zoom_factor() { return double(zoom_)/double(image::tile_size); }
//given x,y co-ordinates of an onscreen pixel, will return the
//location of the hex that this pixel corresponds to. Returns an
//invalid location is the mouse isn't over any valid location.
const gamemap::location hex_clicked_on(int x, int y,
gamemap::location::DIRECTION* nearest_hex=NULL,
gamemap::location::DIRECTION* second_nearest_hex=NULL) const;
//given x,y co-ordinates of a pixel on the map, will return the
//location of the hex that this pixel corresponds to. Returns an
//invalid location if the mouse isn't over any valid location.
const gamemap::location pixel_position_to_hex(int x, int y,
gamemap::location::DIRECTION* nearest_hex=NULL,
gamemap::location::DIRECTION* second_nearest_hex=NULL) const;
//given x,y co-ordinates of the mouse, will return the location of the
//hex in the minimap that the mouse is currently over, or an invalid
//location if the mouse isn't over the minimap.
gamemap::location minimap_location_on(int x, int y);
void get_rect_hex_bounds(SDL_Rect rect, gamemap::location &topleft, gamemap::location &bottomright) const;
//functions to get the on-screen positions of hexes.
// we have a 1 hex border so need to offset the loction with 1
int get_location_x(const gamemap::location& loc) const
{ return map_area().x + (loc.x + 1) * hex_width() - xpos_; }
int get_location_y(const gamemap::location& loc) const
{ return map_area().y + (loc.y + 1) * zoom_ - ypos_ + (is_odd(loc.x) ? zoom_/2 : 0); }
//function to make a screenshot and save it in a default location
void screenshot();
protected:
CVideo& screen_;
const gamemap& map_;
int xpos_, ypos_;
theme theme_;
int zoom_;
};
@ -167,9 +200,6 @@ public:
//even if running behind.
void draw(bool update=true,bool force=false);
//check if pixel x,y is outside specified area
bool outside_area(const SDL_Rect& area, const int x, const int y) const;
//function to display a location as selected. If a unit is in the location,
//and there is no unit in the currently highlighted hex, the unit will be
//displayed in the sidebar.
@ -183,25 +213,6 @@ public:
//over
void highlight_hex(gamemap::location hex);
//given x,y co-ordinates of an onscreen pixel, will return the
//location of the hex that this pixel corresponds to. Returns an
//invalid location is the mouse isn't over any valid location.
const gamemap::location hex_clicked_on(int x, int y,
gamemap::location::DIRECTION* nearest_hex=NULL,
gamemap::location::DIRECTION* second_nearest_hex=NULL) const;
//given x,y co-ordinates of a pixel on the map, will return the
//location of the hex that this pixel corresponds to. Returns an
//invalid location if the mouse isn't over any valid location.
const gamemap::location pixel_position_to_hex(int x, int y,
gamemap::location::DIRECTION* nearest_hex=NULL,
gamemap::location::DIRECTION* second_nearest_hex=NULL) const;
//given x,y co-ordinates of the mouse, will return the location of the
//hex in the minimap that the mouse is currently over, or an invalid
//location if the mouse isn't over the minimap.
gamemap::location minimap_location_on(int x, int y);
//sets the paths that are currently displayed as available for the unit
//to move along. All other paths will be greyed out.
void highlight_reach(const paths &paths_list);
@ -218,13 +229,6 @@ public:
//route does not have to remain valid after being set
void set_route(const paths::route* route);
//functions to get the on-screen positions of hexes.
// we have a 1 hex border so need to offset the loction with 1
int get_location_x(const gamemap::location& loc) const
{ return map_area().x + (loc.x + 1) * hex_width() - xpos_; }
int get_location_y(const gamemap::location& loc) const
{ return map_area().y + (loc.y + 1) * zoom_ - ypos_ + (is_odd(loc.x) ? zoom_/2 : 0); }
//returns the locations of 2 hexes that bind the visible area of the map.
void get_visible_hex_bounds(gamemap::location &topleft, gamemap::location &bottomright) const;
@ -442,8 +446,6 @@ public:
bool in_game() const { return in_game_; }
void draw_bar(const std::string& image, int xpos, int ypos, size_t height, double filled, const SDL_Color& col, fixed_t alpha);
void get_rect_hex_bounds(SDL_Rect rect, gamemap::location &topleft, gamemap::location &bottomright) const;
private:
display(const display&);
void operator=(const display&);
@ -474,7 +476,6 @@ private:
surface get_minimap(int w, int h);
CKey keys_;
int xpos_, ypos_;
std::map<gamemap::location, surface> hex_overlay_;
surface selected_hex_overlay_;