/** * \brief Returns whether a rectangle overlaps the active region of the jumper. * * For a horizontal or vertical jumper, the active region is the whole * bounding box, that is, an 8-pixel thick horizontal or vertical strip. * For a diagonal jumper, the active region is an 8-pixel thick diagonal strip. * * \param rectangle A rectangle. * \param extended_region Whether you want to consider the extended jumper * beyond its bounding box, or to keep it restricted to its bounding box. * \return \c true if the rectangle overlaps the active region of the jumper. */ bool Jumper::overlaps_jumping_region(const Rectangle& rectangle, bool /* extended_region */) const { if (!is_jump_diagonal()) { return overlaps(rectangle); } // Check the 4 corners of the rectangle. Point xy = rectangle.get_xy(); if (is_point_in_diagonal(xy)) { return true; } xy.x += rectangle.get_width() - 1; if (is_point_in_diagonal(xy)) { return true; } xy.y += rectangle.get_height() - 1; if (is_point_in_diagonal(xy)) { return true; } xy.x = rectangle.get_x(); if (is_point_in_diagonal(xy)) { return true; } // Check the two ends of the diagonal. if (get_direction() == 1 || get_direction() == 5) { if (rectangle.contains(get_top_left_x(), get_top_left_y())) { return true; } if (rectangle.contains( get_top_left_x() + get_width() - 1, get_top_left_y() + get_height() - 1)) { return true; } } else { if (rectangle.contains( get_top_left_x() + get_width() - 1, get_top_left_y())) { return true; } if (rectangle.contains( get_top_left_x(), get_top_left_y() + get_height() - 1)) { return true; } } return false; }
/** * \brief Returns whether an entity is correctly placed to start jumping * with this jumper. * \param entity A map entity. * \return \c true if the entity is correctly placed to start the jump. */ bool Jumper::is_in_jump_position(const MapEntity& entity) const { const int direction8 = get_direction(); if (is_jump_diagonal()) { // The sensor's shape is a diagonal bar. return is_point_in_diagonal(entity.get_facing_point((direction8 - 1) / 2)) || is_point_in_diagonal(entity.get_facing_point((direction8 + 1) % 8 / 2)); } // The sensor has one of the four main directions. // Its shape is exactly its rectangle. const int expected_direction4 = direction8 / 2; const Rectangle& facing_point = entity.get_facing_point(expected_direction4); const bool horizontal_jump = is_jump_horizontal(); return overlaps(facing_point.get_x() + (horizontal_jump ? 0 : -8), facing_point.get_y() + (horizontal_jump ? -8 : 0)) && overlaps(facing_point.get_x() + (horizontal_jump ? 0 : 7), facing_point.get_y() + (horizontal_jump ? 7 : 0)); }
/** * @brief Returns whether an entity's collides with this jumper. * * The result depends on the sensor's shape. * * @param entity the entity * @return true if the entity's collides with this jumper */ bool Jumper::test_collision_custom(MapEntity &entity) { if (!entity.is_hero()) { return false; } Hero &hero = (Hero&) entity; int direction8 = get_direction(); // if the sensor's has one of the four main directions, then // its shape is exactly its rectangle if (direction8 % 2 == 0) { int expected_hero_direction4 = direction8 / 2; if (hero.get_ground() == GROUND_DEEP_WATER) { // if the hero is swimming, the jumper can be used the opposite way expected_hero_direction4 = (expected_hero_direction4 + 2) % 4; } if (!hero.is_moving_towards(expected_hero_direction4)) { return false; } bool horizontal = (direction8 % 4 == 0); // horizontal or vertical jumper const Rectangle &facing_point = hero.get_facing_point(expected_hero_direction4); return overlaps(facing_point.get_x() + (horizontal ? 0 : -8), facing_point.get_y() + (horizontal ? -8 : 0)) && overlaps(facing_point.get_x() + (horizontal ? 0 : 7), facing_point.get_y() + (horizontal ? 7 : 0)); } // otherwise, the sensor's shape is a diagonal bar return is_point_in_diagonal(hero.get_facing_point((direction8 - 1) / 2)) || is_point_in_diagonal(hero.get_facing_point((direction8 + 1) % 8 / 2)); }
/** * \brief Returns whether the hero is correctly placed to * start jumping with this jumper. * \param hero The hero. * \param candidate_position The candidate bounding box. * \param extended_region Whether you want to consider the extended jumper * beyond its bounding box, or to keep it restricted to its bounding box. * \return \c true if the hero is correctly placed to start a jump. */ bool Jumper::is_in_jump_position( const Hero& hero, const Rectangle& candidate_position, bool extended_region) const { if (overlaps_jumping_region(candidate_position, extended_region)) { // Overlapping the active region: cannot start a jump from there. return false; } const int direction8 = get_direction(); const int expected_hero_direction4 = direction8 / 2; if (is_jump_diagonal()) { // Diagonal case: The sensor's shape is a diagonal bar. // The player should move toward one of both directions. if (!hero.is_moving_towards(expected_hero_direction4) && !hero.is_moving_towards((expected_hero_direction4 + 1) % 4)) { return false; } // Test if the appropriate corner of the hero crosses the diagonal. Point corner = { candidate_position.get_x() - 1, candidate_position.get_y() - 1 }; if (direction8 == 1 || direction8 == 7) { // Right-up or right-down. corner.x += candidate_position.get_width() + 1; } if (direction8 == 5 || direction8 == 7) { // Left-down or right-down. corner.y += candidate_position.get_height() + 1; } return extended_region ? is_point_in_extended_diagonal(corner) : is_point_in_diagonal(corner); } // Non-diagonal case: the sensor has one of the four main directions. // Its shape is exactly its rectangle. // The player should move toward the jumper's direction. if (!hero.is_moving_towards(expected_hero_direction4)) { return false; } Point facing_point; switch (expected_hero_direction4) { // right case 0: facing_point = { candidate_position.get_x() + 16, candidate_position.get_y() + 8 }; break; // up case 1: facing_point = { candidate_position.get_x() + 8, candidate_position.get_y() - 1 }; break; // left case 2: facing_point = { candidate_position.get_x() - 1, candidate_position.get_y() + 8 }; break; // down case 3: facing_point = { candidate_position.get_x() + 8, candidate_position.get_y() + 16 }; break; default: Debug::die("Invalid direction"); } if (is_jump_horizontal()) { if (extended_region) { // Are we inside the extended strip? return facing_point.x >= get_top_left_x() && facing_point.x < get_top_left_x() + get_width(); } else { // Are we inside the strip and the bounding box? return overlaps(facing_point.x, facing_point.y - 8) && overlaps(facing_point.x, facing_point.y + 7); } } else { // Same thing for a vertical jump. if (extended_region) { // Are we inside the extended strip? return facing_point.y >= get_top_left_y() && facing_point.y < get_top_left_y() + get_height(); } else { return overlaps(facing_point.x - 8, facing_point.y) && overlaps(facing_point.x + 7, facing_point.y); } } }