void set_rectangle_rectangle_constraints(Constraints* constraints, const Rectf& r1, const Rectf& r2, const Vector& addl_ground_movement) { float itop = r1.get_bottom() - r2.get_top(); float ibottom = r2.get_bottom() - r1.get_top(); float ileft = r1.get_right() - r2.get_left(); float iright = r2.get_right() - r1.get_left(); float vert_penetration = std::min(itop, ibottom); float horiz_penetration = std::min(ileft, iright); if(vert_penetration < horiz_penetration) { if(itop < ibottom) { constraints->constrain_bottom(r2.get_top(), addl_ground_movement.y); constraints->hit.bottom = true; constraints->ground_movement += addl_ground_movement; } else { constraints->constrain_top(r2.get_bottom(), addl_ground_movement.y); constraints->hit.top = true; } } else { if(ileft < iright) { constraints->constrain_right(r2.get_left(), addl_ground_movement.x); constraints->hit.right = true; } else { constraints->constrain_left(r2.get_right(), addl_ground_movement.x); constraints->hit.left = true; } } }
/** fills in CollisionHit and Normal vector of 2 intersecting rectangle */ static void get_hit_normal(const Rectf& r1, const Rectf& r2, CollisionHit& hit, Vector& normal) { float itop = r1.get_bottom() - r2.get_top(); float ibottom = r2.get_bottom() - r1.get_top(); float ileft = r1.get_right() - r2.get_left(); float iright = r2.get_right() - r1.get_left(); float vert_penetration = std::min(itop, ibottom); float horiz_penetration = std::min(ileft, iright); if(vert_penetration < horiz_penetration) { if(itop < ibottom) { hit.bottom = true; normal.y = vert_penetration; } else { hit.top = true; normal.y = -vert_penetration; } } else { if(ileft < iright) { hit.right = true; normal.x = horiz_penetration; } else { hit.left = true; normal.x = -horiz_penetration; } } }
void Sector::collision_tilemap(collision::Constraints* constraints, const Vector& movement, const Rectf& dest, MovingObject& object) const { // calculate rectangle where the object will move float x1 = dest.get_left(); float x2 = dest.get_right(); float y1 = dest.get_top(); float y2 = dest.get_bottom(); for(auto i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) { TileMap* solids = *i; // test with all tiles in this rectangle Rect test_tiles = solids->get_tiles_overlapping(Rectf(x1, y1, x2, y2)); 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; // skip non-solid tiles if(!tile->is_solid ()) continue; Rectf tile_bbox = solids->get_tile_bbox(x, y); /* If the tile is a unisolid tile, the "is_solid()" function above * didn't do a thorough check. Calculate the position and (relative) * movement of the object and determine whether or not the tile is * solid with regard to those parameters. */ if(tile->is_unisolid ()) { Vector relative_movement = movement - solids->get_movement(/* actual = */ true); if (!tile->is_solid (tile_bbox, object.get_bbox(), relative_movement)) continue; } /* if (tile->is_unisolid ()) */ if(tile->is_slope ()) { // slope tile AATriangle triangle; int slope_data = tile->getData(); if (solids->get_drawing_effect() & VERTICAL_FLIP) slope_data = AATriangle::vertical_flip(slope_data); triangle = AATriangle(tile_bbox, slope_data); collision::rectangle_aatriangle(constraints, dest, triangle, solids->get_movement(/* actual = */ false)); } else { // normal rectangular tile check_collisions(constraints, movement, dest, tile_bbox, NULL, NULL, solids->get_movement(/* actual = */ false)); } } } } }
bool contains(const Rectf& other) const { // FIXME: This is overlaps(), not contains()! if (m_p1.x >= other.get_right() || other.get_left() >= get_right()) return false; if (m_p1.y >= other.get_bottom() || other.get_top() >= get_bottom()) return false; return true; }
void EditorToolboxWidget::update_selection() { Rectf select = normalize_selection(); m_tiles->m_tiles.clear(); m_tiles->m_width = static_cast<int>(select.get_width() + 1); m_tiles->m_height = static_cast<int>(select.get_height() + 1); int size = static_cast<int>(m_active_tilegroup->tiles.size()); for (int y = static_cast<int>(select.get_top()); y <= static_cast<int>(select.get_bottom()); y++) { for (int x = static_cast<int>(select.get_left()); x <= static_cast<int>(select.get_right()); x++) { int tile_pos = y*4 + x + m_starting_tile; if (tile_pos < size && tile_pos >= 0) { m_tiles->m_tiles.push_back(m_active_tilegroup->tiles[tile_pos]); } else { m_tiles->m_tiles.push_back(0); } } } }
bool rectangle_aatriangle(Constraints* constraints, const Rectf& rect, const AATriangle& triangle, const Vector& addl_ground_movement) { if(!intersects(rect, (const Rectf&) triangle)) return false; Vector normal; float c = 0.0; Vector p1; Rectf area; switch(triangle.dir & AATriangle::DEFORM_MASK) { case 0: area.p1 = triangle.bbox.p1; area.p2 = triangle.bbox.p2; break; case AATriangle::DEFORM_BOTTOM: area.p1 = Vector(triangle.bbox.p1.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2); area.p2 = triangle.bbox.p2; break; case AATriangle::DEFORM_TOP: area.p1 = triangle.bbox.p1; area.p2 = Vector(triangle.bbox.p2.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2); break; case AATriangle::DEFORM_LEFT: area.p1 = triangle.bbox.p1; area.p2 = Vector(triangle.bbox.p1.x + triangle.bbox.get_width()/2, triangle.bbox.p2.y); break; case AATriangle::DEFORM_RIGHT: 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); } switch(triangle.dir & AATriangle::DIRECTION_MASK) { case AATriangle::SOUTHWEST: p1 = Vector(rect.p1.x, rect.p2.y); makePlane(area.p1, area.p2, normal, c); break; case AATriangle::NORTHEAST: p1 = Vector(rect.p2.x, rect.p1.y); makePlane(area.p2, area.p1, normal, c); break; case AATriangle::SOUTHEAST: p1 = rect.p2; makePlane(Vector(area.p1.x, area.p2.y), Vector(area.p2.x, area.p1.y), normal, c); break; case AATriangle::NORTHWEST: p1 = rect.p1; makePlane(Vector(area.p2.x, area.p1.y), Vector(area.p1.x, area.p2.y), normal, c); break; default: assert(false); } float n_p1 = -(normal * p1); float depth = n_p1 - c; if(depth < 0) return false; #if 0 std::cout << "R: " << rect << " Tri: " << triangle << "\n"; std::cout << "Norm: " << normal << " Depth: " << depth << "\n"; #endif Vector outvec = normal * (depth + 0.2f); const float RDELTA = 3; if(p1.x < area.p1.x - RDELTA || p1.x > area.p2.x + RDELTA || p1.y < area.p1.y - RDELTA || p1.y > area.p2.y + RDELTA) { set_rectangle_rectangle_constraints(constraints, rect, area); } else { if(outvec.x < 0) { constraints->constrain_right(rect.get_right() + outvec.x, addl_ground_movement.x); constraints->hit.right = true; } else { constraints->constrain_left(rect.get_left() + outvec.x, addl_ground_movement.x); constraints->hit.left = true; } if(outvec.y < 0) { constraints->constrain_bottom(rect.get_bottom() + outvec.y, addl_ground_movement.y); constraints->hit.bottom = true; constraints->ground_movement += addl_ground_movement; } else { constraints->constrain_top(rect.get_top() + outvec.y, addl_ground_movement.y); constraints->hit.top = true; } constraints->hit.slope_normal = normal; } return true; }
/* Check whether the object is already *in* the tile. If so, the tile * is non-solid. Otherwise, if the object is "above" (south slopes) * or "below" (north slopes), the tile will be solid. */ bool Tile::check_position_unisolid (const Rectf& obj_bbox, const Rectf& tile_bbox) const { int slope_info; float tile_x; float tile_y; float gradient; float delta_x; float delta_y; float obj_x = 0.0; float obj_y = 0.0; /* If this is not a slope, this is - again - easy */ if (!this->is_slope()) { int dir = this->getData() & Tile::UNI_DIR_MASK; return ((dir == Tile::UNI_DIR_NORTH) && ((obj_bbox.get_bottom() - SHIFT_DELTA) <= tile_bbox.get_top() )) || ((dir == Tile::UNI_DIR_SOUTH) && ((obj_bbox.get_top() + SHIFT_DELTA) >= tile_bbox.get_bottom())) || ((dir == Tile::UNI_DIR_WEST ) && ((obj_bbox.get_right() - SHIFT_DELTA) <= tile_bbox.get_left() )) || ((dir == Tile::UNI_DIR_EAST ) && ((obj_bbox.get_left() + SHIFT_DELTA) >= tile_bbox.get_right() )); } /* There are 20 different cases. For each case, calculate a line that * describes the slope's surface. The line is defined by x, y, and m, the * gradient. */ slope_info = this->getData(); switch (slope_info & (AATriangle::DIRECTION_MASK | AATriangle::DEFORM_MASK)) { case AATriangle::SOUTHWEST: case AATriangle::SOUTHWEST | AATriangle::DEFORM_TOP: case AATriangle::SOUTHWEST | AATriangle::DEFORM_LEFT: case AATriangle::NORTHEAST: case AATriangle::NORTHEAST | AATriangle::DEFORM_TOP: case AATriangle::NORTHEAST | AATriangle::DEFORM_LEFT: tile_x = tile_bbox.get_left(); tile_y = tile_bbox.get_top(); gradient = 1.0; break; case AATriangle::SOUTHEAST: case AATriangle::SOUTHEAST | AATriangle::DEFORM_TOP: case AATriangle::SOUTHEAST | AATriangle::DEFORM_RIGHT: case AATriangle::NORTHWEST: case AATriangle::NORTHWEST | AATriangle::DEFORM_TOP: case AATriangle::NORTHWEST | AATriangle::DEFORM_RIGHT: tile_x = tile_bbox.get_right(); tile_y = tile_bbox.get_top(); gradient = -1.0; break; case AATriangle::SOUTHEAST | AATriangle::DEFORM_BOTTOM: case AATriangle::SOUTHEAST | AATriangle::DEFORM_LEFT: case AATriangle::NORTHWEST | AATriangle::DEFORM_BOTTOM: case AATriangle::NORTHWEST | AATriangle::DEFORM_LEFT: tile_x = tile_bbox.get_left(); tile_y = tile_bbox.get_bottom(); gradient = -1.0; break; case AATriangle::SOUTHWEST | AATriangle::DEFORM_BOTTOM: case AATriangle::SOUTHWEST | AATriangle::DEFORM_RIGHT: case AATriangle::NORTHEAST | AATriangle::DEFORM_BOTTOM: case AATriangle::NORTHEAST | AATriangle::DEFORM_RIGHT: tile_x = tile_bbox.get_right(); tile_y = tile_bbox.get_bottom(); gradient = 1.0; break; default: assert (23 == 42); return 0; } /* delta_x, delta_y: Gradient aware version of SHIFT_DELTA. Here, we set the * sign of the values only. Also, we determine here which corner of the * object's bounding box is the interesting one for us. */ delta_x = 1.0 * SHIFT_DELTA; delta_y = 1.0 * SHIFT_DELTA; switch (slope_info & AATriangle::DIRECTION_MASK) { case AATriangle::SOUTHWEST: delta_x *= 1.0; delta_y *= -1.0; obj_x = obj_bbox.get_left(); obj_y = obj_bbox.get_bottom(); break; case AATriangle::SOUTHEAST: delta_x *= -1.0; delta_y *= -1.0; obj_x = obj_bbox.get_right(); obj_y = obj_bbox.get_bottom(); break; case AATriangle::NORTHWEST: delta_x *= 1.0; delta_y *= 1.0; obj_x = obj_bbox.get_left(); obj_y = obj_bbox.get_top(); break; case AATriangle::NORTHEAST: delta_x *= -1.0; delta_y *= 1.0; obj_x = obj_bbox.get_right(); obj_y = obj_bbox.get_top(); break; } /* Adapt the delta_x, delta_y and the gradient for the 26.6 deg and 63.4 deg * cases. */ switch (slope_info & AATriangle::DEFORM_MASK) { case 0: delta_x *= .70710678118654752440; /* 1/sqrt(2) */ delta_y *= .70710678118654752440; /* 1/sqrt(2) */ break; case AATriangle::DEFORM_BOTTOM: case AATriangle::DEFORM_TOP: delta_x *= .44721359549995793928; /* 1/sqrt(5) */ delta_y *= .89442719099991587856; /* 2/sqrt(5) */ gradient *= 0.5; break; case AATriangle::DEFORM_LEFT: case AATriangle::DEFORM_RIGHT: delta_x *= .89442719099991587856; /* 2/sqrt(5) */ delta_y *= .44721359549995793928; /* 1/sqrt(5) */ gradient *= 2.0; break; } /* With a south slope, check if all points are above the line. If one point * isn't, the slope is not solid. => You can pass through a south-slope from * below but not from above. */ if (((slope_info & AATriangle::DIRECTION_MASK) == AATriangle::SOUTHWEST) || ((slope_info & AATriangle::DIRECTION_MASK) == AATriangle::SOUTHEAST)) { return !is_below_line(tile_x, tile_y, gradient, obj_x + delta_x, obj_y + delta_y); } /* northwest or northeast. Same as above, but inverted. You can pass from top * to bottom but not vice versa. */ else { return !is_above_line (tile_x, tile_y, gradient, obj_x + delta_x, obj_y + delta_y); } } /* int check_position_unisolid */
void Background::draw_image(DrawingContext& context, const Vector& pos_) { Sizef level(Sector::current()->get_width(), Sector::current()->get_height()); Sizef screen(SCREEN_WIDTH, SCREEN_HEIGHT); Sizef parallax_image_size = (1.0f - speed) * screen + level * speed; Rectf cliprect = context.get_cliprect(); int start_x = static_cast<int>(floorf((cliprect.get_left() - (pos_.x - image->get_width() /2.0f)) / image->get_width())); int end_x = static_cast<int>(ceilf((cliprect.get_right() - (pos_.x + image->get_width() /2.0f)) / image->get_width()))+1; int start_y = static_cast<int>(floorf((cliprect.get_top() - (pos_.y - image->get_height()/2.0f)) / image->get_height())); int end_y = static_cast<int>(ceilf((cliprect.get_bottom() - (pos_.y + image->get_height()/2.0f)) / image->get_height()))+1; switch(alignment) { case LEFT_ALIGNMENT: for(int y = start_y; y < end_y; ++y) { Vector p(pos_.x - parallax_image_size.width / 2.0f, pos_.y + y * image->get_height() - image->get_height() / 2.0f); context.draw_surface(image, p, layer); } break; case RIGHT_ALIGNMENT: for(int y = start_y; y < end_y; ++y) { Vector p(pos_.x + parallax_image_size.width / 2.0f - image->get_width(), pos_.y + y * image->get_height() - image->get_height() / 2.0f); context.draw_surface(image, p, layer); } break; case TOP_ALIGNMENT: for(int x = start_x; x < end_x; ++x) { Vector p(pos_.x + x * image->get_width() - image->get_width() / 2.0f, pos_.y - parallax_image_size.height / 2.0f); context.draw_surface(image, p, layer); } break; case BOTTOM_ALIGNMENT: for(int x = start_x; x < end_x; ++x) { Vector p(pos_.x + x * image->get_width() - image->get_width() / 2.0f, pos_.y - image->get_height() + parallax_image_size.height / 2.0f); context.draw_surface(image, p, layer); } break; case NO_ALIGNMENT: for(int y = start_y; y < end_y; ++y) for(int x = start_x; x < end_x; ++x) { Vector p(pos_.x + x * image->get_width() - image->get_width()/2, pos_.y + y * image->get_height() - image->get_height()/2); if (image_top.get() != NULL && (y < 0)) { context.draw_surface(image_top, p, layer); } else if (image_bottom.get() != NULL && (y > 0)) { context.draw_surface(image_bottom, p, layer); } else { context.draw_surface(image, p, layer); } } break; } }
/** r1 is supposed to be moving, r2 a solid object */ void check_collisions(collision::Constraints* constraints, const Vector& obj_movement, const Rectf& obj_rect, const Rectf& other_rect, GameObject* object = NULL, MovingObject* other = NULL, const Vector& other_movement = Vector(0,0)) { if(!collision::intersects(obj_rect, other_rect)) return; MovingObject *moving_object = dynamic_cast<MovingObject*> (object); CollisionHit dummy; if(other != NULL && object != NULL && !other->collides(*object, dummy)) return; if(moving_object != NULL && other != NULL && !moving_object->collides(*other, dummy)) return; // calculate intersection float itop = obj_rect.get_bottom() - other_rect.get_top(); float ibottom = other_rect.get_bottom() - obj_rect.get_top(); float ileft = obj_rect.get_right() - other_rect.get_left(); float iright = other_rect.get_right() - obj_rect.get_left(); if(fabsf(obj_movement.y) > fabsf(obj_movement.x)) { if(ileft < SHIFT_DELTA) { constraints->constrain_right(other_rect.get_left(), other_movement.x); return; } else if(iright < SHIFT_DELTA) { constraints->constrain_left(other_rect.get_right(), other_movement.x); return; } } else { // shiftout bottom/top if(itop < SHIFT_DELTA) { constraints->constrain_bottom(other_rect.get_top(), other_movement.y); return; } else if(ibottom < SHIFT_DELTA) { constraints->constrain_top(other_rect.get_bottom(), other_movement.y); return; } } constraints->ground_movement += other_movement; if(other != NULL && object != NULL) { HitResponse response = other->collision(*object, dummy); if(response == ABORT_MOVE) return; if(other->get_movement() != Vector(0, 0)) { // TODO what todo when we collide with 2 moving objects?!? constraints->ground_movement += other->get_movement(); } } float vert_penetration = std::min(itop, ibottom); float horiz_penetration = std::min(ileft, iright); if(vert_penetration < horiz_penetration) { if(itop < ibottom) { constraints->constrain_bottom(other_rect.get_top(), other_movement.y); constraints->hit.bottom = true; } else { constraints->constrain_top(other_rect.get_bottom(), other_movement.y); constraints->hit.top = true; } } else { if(ileft < iright) { constraints->constrain_right(other_rect.get_left(), other_movement.x); constraints->hit.right = true; } else { constraints->constrain_left(other_rect.get_right(), other_movement.x); constraints->hit.left = true; } } }