- Combine TileMap::{x,y}_offset into a vector TileMap::offset.
- Factor out some widely duplicated code into new TileMap methods
get_tile_position, get_bbox, get_tile_bbox, get_tiles_overlapping.
- AATriangle contains a Rectf instead of two vectors.
- Some simplification to Sector::is_free_of_tiles.
- BadGuy::might_fall passed a zero-width rectangle to
Sector::is_free_of_tiles. With the new code, such a rectangle can slip
between two tiles without overlapping either one, giving an incorrect
result. So change the BadGuy::might_fall rectangle to have width 1.
Thanks to Matt McCutchen for this patch.
SVN-Revision: 6297
float y2 = bbox.p2.y + 1 + height;
if (dir == LEFT) {
x1 = bbox.p1.x - 1;
- x2 = bbox.p1.x - 1;
+ x2 = bbox.p1.x;
} else {
- x1 = bbox.p2.x + 1;
+ x1 = bbox.p2.x;
x2 = bbox.p2.x + 1;
}
return Sector::current()->is_free_of_statics(Rectf(x1, y1, x2, y2));
#ifndef HEADER_SUPERTUX_MATH_AATRIANGLE_HPP
#define HEADER_SUPERTUX_MATH_AATRIANGLE_HPP
-#include "math/vector.hpp"
+#include "math/rectf.hpp"
/**
* An axis-aligned triangle (ie. a triangle where 2 sides are parallel to the x-
public:
AATriangle() :
- p1(),
- p2(),
+ bbox(),
dir(SOUTHWEST)
{
}
- AATriangle(const Vector& v1, const Vector& v2, int newdir) :
- p1(v1),
- p2(v2),
+ AATriangle(const Rectf& newbbox, int newdir) :
+ bbox(newbbox),
dir(newdir)
{
}
- float get_width() const
- {
- return p2.x - p1.x;
- }
-
- float get_height() const
- {
- return p2.y - p1.y;
- }
-
public:
- Vector p1;
- Vector p2;
+ Rectf bbox;
int dir;
};
for(std::list<TileMap*>::const_iterator i = Sector::current()->solid_tilemaps.begin(); i != Sector::current()->solid_tilemaps.end(); i++) {
TileMap* solids = *i;
+ // FIXME Handle a nonzero tilemap offset
for(int x = starttilex; x*32 < max_x; ++x) {
for(int y = starttiley; y*32 < max_y; ++y) {
const Tile* tile = solids->get_tile(x, y);
if(! (tile->getAttributes() & (Tile::WATER | Tile::SOLID)))
continue;
+ Rectf rect = solids->get_tile_bbox(x, y);
if(tile->getAttributes() & Tile::SLOPE) { // slope tile
- AATriangle triangle;
- Vector p1(x*32, y*32);
- Vector p2((x+1)*32, (y+1)*32);
- triangle = AATriangle(p1, p2, tile->getData());
+ AATriangle triangle = AATriangle(rect, tile->getData());
if(rectangle_aatriangle(&constraints, dest, triangle)) {
if(tile->getAttributes() & Tile::WATER)
water = true;
}
} else { // normal rectangular tile
- Rectf rect(x*32, y*32, (x+1)*32, (y+1)*32);
if(intersects(dest, rect)) {
if(tile->getAttributes() & Tile::WATER)
water = true;
width(0),
height(0),
z_pos(0),
- x_offset(0),
- y_offset(0),
+ offset(Vector(0,0)),
movement(0,0),
drawing_effect(NO_EFFECT),
alpha(1.0),
width(-1),
height(-1),
z_pos(0),
- x_offset(0),
- y_offset(0),
+ offset(Vector(0,0)),
movement(Vector(0,0)),
drawing_effect(NO_EFFECT),
alpha(1.0),
path->read(*pathLisp);
walker.reset(new PathWalker(path.get(), /*running*/false));
Vector v = path->get_base();
- set_x_offset(v.x);
- set_y_offset(v.y);
+ set_offset(v);
}
std::string draw_target_s = "normal";
width(0),
height(0),
z_pos(z_pos),
- x_offset(0),
- y_offset(0),
+ offset(Vector(0,0)),
movement(Vector(0,0)),
drawing_effect(NO_EFFECT),
alpha(1.0),
// if we have a path to follow, follow it
if (walker.get()) {
Vector v = walker->advance(elapsed_time);
- movement = Vector(v.x-get_x_offset(), std::max(0.0f,v.y-get_y_offset()));
- set_x_offset(v.x);
- set_y_offset(v.y);
+ movement = Vector(v.x-get_offset().x, std::max(0.0f,v.y-get_offset().y));
+ set_offset(v);
}
}
context.set_translation(Vector(int(trans_x * speed_x),
int(trans_y * speed_y)));
- int tsx = int((context.get_translation().x - x_offset) / 32); // tilestartindex x
- int tsy = int((context.get_translation().y - y_offset) / 32); // tilestartindex y
- tsx = std::max(tsx, 0);
- tsy = std::max(tsy, 0);
- float start_x = tsx * 32 + x_offset;
- float start_y = tsy * 32 + y_offset;
- float end_x = start_x + SCREEN_WIDTH + 32;
- float end_y = start_y + SCREEN_HEIGHT + 32;
+ Rectf draw_rect = Rectf(context.get_translation(),
+ context.get_translation() + Vector(SCREEN_WIDTH, SCREEN_HEIGHT));
+ Rect t_draw_rect = get_tiles_overlapping(draw_rect);
+ Vector start = get_tile_position(t_draw_rect.left, t_draw_rect.top);
Vector pos;
int tx, ty;
- for(pos.x = start_x, tx = tsx; (pos.x < end_x) && (tx < width); pos.x += 32, ++tx) {
- for(pos.y = start_y, ty = tsy; (pos.y < end_y) && (ty < height); pos.y += 32, ++ty) {
+ for(pos.x = start.x, tx = t_draw_rect.left; tx < t_draw_rect.right; pos.x += 32, ++tx) {
+ for(pos.y = start.y, ty = t_draw_rect.top; ty < t_draw_rect.bottom; pos.y += 32, ++ty) {
int index = ty*width + tx;
assert (index >= 0);
assert (index < (width * height));
width = new_width;
}
+Rect
+TileMap::get_tiles_overlapping(const Rectf &rect) const
+{
+ Rectf rect2 = rect;
+ rect2.move(-offset);
+
+ int t_left = std::max(0 , int(floorf(rect2.get_left () / 32)));
+ int t_right = std::min(width , int(ceilf (rect2.get_right () / 32)));
+ int t_top = std::max(0 , int(floorf(rect2.get_top () / 32)));
+ int t_bottom = std::min(height, int(ceilf (rect2.get_bottom() / 32)));
+ return Rect(t_left, t_top, t_right, t_bottom);
+}
+
void
TileMap::set_solid(bool solid)
{
uint32_t
TileMap::get_tile_id_at(const Vector& pos) const
{
- return get_tile_id(int(pos.x - x_offset)/32, int(pos.y - y_offset)/32);
+ Vector xy = (pos - offset) / 32;
+ return get_tile_id(int(xy.x), int(xy.y));
}
const Tile*
void
TileMap::change_at(const Vector& pos, uint32_t newtile)
{
- change(int(pos.x - x_offset)/32, int(pos.y - y_offset)/32, newtile);
+ Vector xy = (pos - offset) / 32;
+ change(int(xy.x), int(xy.y), newtile);
}
void
size_t get_height() const
{ return height; }
- float get_x_offset() const
- { return x_offset; }
-
- float get_y_offset() const
- { return y_offset; }
+ Vector get_offset() const
+ { return offset; }
const Vector& get_movement() const
{
return movement;
}
- void set_x_offset(float x_offset)
- { this->x_offset = x_offset; }
+ void set_offset(const Vector &offset)
+ { this->offset = offset; }
+
+ /* Returns the position of the upper-left corner of
+ * tile (x, y) in the sector. */
+ Vector get_tile_position(int x, int y) const
+ { return offset + Vector(x,y) * 32; }
+
+ Rectf get_bbox() const
+ { return Rectf(get_tile_position(0, 0), get_tile_position(width, height)); }
+
+ Rectf get_tile_bbox(int x, int y) const
+ { return Rectf(get_tile_position(x, y), get_tile_position(x+1, y+1)); }
- void set_y_offset(float y_offset)
- { this->y_offset = y_offset; }
+ /* Returns the half-open rectangle of (x, y) tile indices
+ * that overlap the given rectangle in the sector. */
+ Rect get_tiles_overlapping(const Rectf &rect) const;
int get_layer() const
{ return z_pos; }
float speed_y;
int width, height;
int z_pos;
- float x_offset;
- float y_offset;
+ Vector offset;
Vector movement; /**< The movement that happened last frame */
DrawingEffect drawing_effect;
Rectf area;
switch(triangle.dir & AATriangle::DEFORM_MASK) {
case 0:
- area.p1 = triangle.p1;
- area.p2 = triangle.p2;
+ area.p1 = triangle.bbox.p1;
+ area.p2 = triangle.bbox.p2;
break;
case AATriangle::DEFORM1:
- area.p1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
- area.p2 = triangle.p2;
+ area.p1 = Vector(triangle.bbox.p1.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2);
+ area.p2 = triangle.bbox.p2;
break;
case AATriangle::DEFORM2:
- area.p1 = triangle.p1;
- area.p2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
+ area.p1 = triangle.bbox.p1;
+ area.p2 = Vector(triangle.bbox.p2.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2);
break;
case AATriangle::DEFORM3:
- area.p1 = triangle.p1;
- area.p2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
+ area.p1 = triangle.bbox.p1;
+ area.p2 = Vector(triangle.bbox.p1.x + triangle.bbox.get_width()/2, triangle.bbox.p2.y);
break;
case AATriangle::DEFORM4:
- area.p1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
- area.p2 = triangle.p2;
+ area.p1 = Vector(triangle.bbox.p1.x + triangle.bbox.get_width()/2, triangle.bbox.p1.y);
+ area.p2 = triangle.bbox.p2;
break;
default:
assert(false);
for(size_t y=0; y < solids->get_height(); ++y) {
uint32_t id = solids->get_tile_id(x, y);
const Tile *tile = solids->get_tile(x, y);
- Vector pos(solids->get_x_offset() + x*32, solids->get_y_offset() + y*32);
+ Vector pos = solids->get_tile_position(x, y);
if(id == 112) {
add_object(new InvisibleBlock(pos));
for(size_t x=0; x < tm->get_width(); ++x) {
for(size_t y=0; y < tm->get_height(); ++y) {
uint32_t id = tm->get_tile_id(x, y);
- Vector pos(tm->get_x_offset() + x*32, tm->get_y_offset() + y*32);
- Vector center(pos.x + 16, pos.y + 16);
+ Vector pos = tm->get_tile_position(x, y);
+ Vector center = pos + Vector(16, 16);
// torch
if (id == 1517) {
TileMap* solids = *i;
// test with all tiles in this rectangle
- int starttilex = int(x1 - solids->get_x_offset()) / 32;
- int starttiley = int(y1 - solids->get_y_offset()) / 32;
- int max_x = int(x2 - solids->get_x_offset());
- int max_y = int(y2+1 - solids->get_y_offset());
+ Rect test_tiles = solids->get_tiles_overlapping(Rectf(x1, y1, x2, y2));
- for(int x = starttilex; x*32 < max_x; ++x) {
- for(int y = starttiley; y*32 < max_y; ++y) {
+ for(int x = test_tiles.left; x < test_tiles.right; ++x) {
+ for(int y = test_tiles.top; y < test_tiles.bottom; ++y) {
const Tile* tile = solids->get_tile(x, y);
if(!tile)
continue;
continue;
}
+ Rectf rect = solids->get_tile_bbox(x, y);
if(tile->getAttributes() & Tile::SLOPE) { // slope tile
AATriangle triangle;
- Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
- Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
int slope_data = tile->getData();
if (solids->get_drawing_effect() == VERTICAL_FLIP)
slope_data = AATriangle::vertical_flip(slope_data);
- triangle = AATriangle(p1, p2, slope_data);
+ triangle = AATriangle(rect, slope_data);
collision::rectangle_aatriangle(constraints, dest, triangle, solids->get_movement());
} else { // normal rectangular tile
- Rectf rect(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset(), (x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
check_collisions(constraints, movement, dest, rect, NULL, NULL, solids->get_movement());
}
}
TileMap* solids = *i;
// test with all tiles in this rectangle
- int starttilex = int(x1 - solids->get_x_offset()) / 32;
- int starttiley = int(y1 - solids->get_y_offset()) / 32;
- int max_x = int(x2 - solids->get_x_offset());
- int max_y = int(y2+1 - solids->get_y_offset());
- int max_y_ice = int(max_y + SHIFT_DELTA);
+ Rect test_tiles = solids->get_tiles_overlapping(Rectf(x1, y1, x2, y2));
+ // For ice (only), add a little fudge to recognize tiles Tux is standing on.
+ Rect test_tiles_ice = solids->get_tiles_overlapping(Rectf(x1, y1, x2, y2 + SHIFT_DELTA));
- for(int x = starttilex; x*32 < max_x; ++x) {
+ for(int x = test_tiles.left; x < test_tiles.right; ++x) {
int y;
- for(y = starttiley; y*32 < max_y; ++y) {
+ for(y = test_tiles.top; y < test_tiles.bottom; ++y) {
const Tile* tile = solids->get_tile(x, y);
if(!tile)
continue;
result |= tile->getAttributes();
}
- for(; y*32 < max_y_ice; ++y) {
+ for(; y < test_tiles_ice.bottom; ++y) {
const Tile* tile = solids->get_tile(x, y);
if(!tile)
continue;
TileMap* solids = *i;
// test with all tiles in this rectangle
- int starttilex = int(rect.p1.x - solids->get_x_offset()) / 32;
- int starttiley = int(rect.p1.y - solids->get_y_offset()) / 32;
- int max_x = int(rect.p2.x - solids->get_x_offset());
- int max_y = int(rect.p2.y - solids->get_y_offset());
+ Rect test_tiles = solids->get_tiles_overlapping(rect);
- for(int x = starttilex; x*32 <= max_x; ++x) {
- for(int y = starttiley; y*32 <= max_y; ++y) {
+ for(int x = test_tiles.left; x < test_tiles.right; ++x) {
+ for(int y = test_tiles.top; y < test_tiles.bottom; ++y) {
const Tile* tile = solids->get_tile(x, y);
if(!tile) continue;
+ if(!(tile->getAttributes() & Tile::SOLID))
+ continue;
+ if((tile->getAttributes() & Tile::UNISOLID) && ignoreUnisolid)
+ continue;
if(tile->getAttributes() & Tile::SLOPE) {
AATriangle triangle;
- Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
- Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
- triangle = AATriangle(p1, p2, tile->getData());
+ Rectf tbbox = solids->get_tile_bbox(x, y);
+ triangle = AATriangle(tbbox, tile->getData());
Constraints constraints;
- if(collision::rectangle_aatriangle(&constraints, rect, triangle) && (!ignoreUnisolid || !(tile->getAttributes() & Tile::UNISOLID))) return false;
+ if(!collision::rectangle_aatriangle(&constraints, rect, triangle))
+ continue;
}
- if((tile->getAttributes() & Tile::SOLID) && (!ignoreUnisolid || !(tile->getAttributes() & Tile::UNISOLID))) return false;
+ // We have a solid tile that overlaps the given rectangle.
+ return false;
}
}
}
{
for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
TileMap* solids = *i;
- bool horizontally = ((rect.p2.x >= 0 + solids->get_x_offset()) && (rect.p1.x <= solids->get_width() * 32 + solids->get_x_offset()));
- bool vertically = (rect.p1.y <= solids->get_height() * 32 + solids->get_y_offset());
- if (horizontally && vertically)
+ Rectf bbox = solids->get_bbox();
+ bbox.p1.y = -INFINITY; // pretend the tilemap extends infinitely far upwards
+
+ if (bbox.contains(rect))
return true;
}
return false;
for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
i != solid_tilemaps.end(); i++) {
TileMap* solids = *i;
- if ((solids->get_width() * 32 + solids->get_x_offset()) > width) {
- width = solids->get_width() * 32 + solids->get_x_offset();
- }
+ width = std::max(width, solids->get_bbox().get_right());
}
return width;
for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
i != solid_tilemaps.end(); i++) {
TileMap* solids = *i;
- if ((solids->get_height() * 32 + solids->get_y_offset()) > height) {
- height = solids->get_height() * 32 + solids->get_y_offset();
- }
+ height = std::max(height, solids->get_bbox().get_bottom());
}
return height;