Пример #1
0
/**
 * \brief Returns the subarea in which an entity tooking these stairs can be
 * displayed.
 * \param way The way you are taking these stairs.
 * \return The subarea of the map where the entity displaying should be
 * restricted to.
 */
Rectangle Stairs::get_clipping_rectangle(Way way) {

  if (subtype == INSIDE_FLOOR || subtype == STRAIGHT_UPSTAIRS) {
    return Rectangle(0, 0, 0, 0); // no restriction
  }

  Rectangle clipping_rectangle(0, 0, get_map().get_width(), get_map().get_height());
  bool north = get_direction() == 1; // north or south

  // Hide the hero after the north of south edge.
  if (north) {
    clipping_rectangle.set_y(get_top_left_y() - 8);
    clipping_rectangle.set_height(48);
  }
  else { // south
    clipping_rectangle.set_y(get_top_left_y() - 32);
    clipping_rectangle.set_height(48);
  }

  // Spiral staircase: also hide a side.
  if ((subtype == SPIRAL_DOWNSTAIRS && north)
      || (subtype == SPIRAL_UPSTAIRS && !north)) {
     // north downstairs or south upstairs: hide the west side
    clipping_rectangle.set_x(get_top_left_x());
    clipping_rectangle.set_width(32);
  }
  else if ((subtype == SPIRAL_UPSTAIRS && north)
      || (subtype == SPIRAL_DOWNSTAIRS && !north)) {
     // north downstairs or south upstairs: hide the east side
    clipping_rectangle.set_x(get_top_left_x() - 16);
    clipping_rectangle.set_width(32);
  }

  return clipping_rectangle;
}
Пример #2
0
/**
 * @brief Displays the tile on the map.
 */
void DynamicTile::display_on_map() {

  // FIXME this code is duplicated from Tile
  Surface* map_surface = get_map().get_visible_surface();

  const Rectangle& camera_position = get_map().get_camera_position();
  Rectangle dst(0, 0);

  int limit_x = get_top_left_x() - camera_position.get_x() + get_width();
  int limit_y = get_top_left_y() - camera_position.get_y() + get_height();

  for (int y = get_top_left_y() - camera_position.get_y();
      y < limit_y;
      y += tile_pattern->get_height()) {

    if (y <= 240 && y + tile_pattern->get_height() > 0) {
      dst.set_y(y);

      for (int x = get_top_left_x() - camera_position.get_x();
          x < limit_x;
          x += tile_pattern->get_width()) {

        if (x <= 320 && x + tile_pattern->get_width() > 0) {
          dst.set_x(x);
          tile_pattern->display(map_surface, dst, get_map().get_tileset(), get_map().get_camera_position());
        }
      }
    }
  }
}
Пример #3
0
/**
 * \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;
}
Пример #4
0
/**
 * @brief Displays the entity on the map.
 *
 * This is a redefinition of MapEntity::display_on_map() to also display the twinkling star
 * which has a special position.
 */
void CrystalSwitch::display_on_map() {

  // display the crystal switch
  MapEntity::display_on_map();

  // display the star
  get_map().display_sprite(*star_sprite, get_top_left_x() + star_xy.get_x(), get_top_left_y() + star_xy.get_y());
}
Пример #5
0
/**
 * \brief Draws the tile on the specified surface.
 * \param dst_surface the destination surface
 * \param viewport coordinates of the top-left corner of dst_surface
 * relative to the map
 */
void Tile::draw(Surface& dst_surface, const Rectangle& viewport) {

  Rectangle dst_position(get_top_left_x() - viewport.get_x(),
      get_top_left_y() - viewport.get_y(),
      get_width(), get_height());

  tile_pattern->fill_surface(dst_surface, dst_position,
      get_map().get_tileset(), viewport);
}
Пример #6
0
/**
 * \brief This function is called when another entity overlaps this crystal block.
 * \param entity_overlapping the other entity
 * \param collision_mode the collision mode that detected the collision
 */
void CrystalBlock::notify_collision(MapEntity& entity_overlapping, CollisionMode collision_mode) {

  if (entity_overlapping.is_hero() && is_raised()) {

    // see if we have to make fim fall

    Hero& hero = static_cast<Hero&>(entity_overlapping);
    if (hero.can_control_movement()) {

      Rectangle collision_box = hero.get_bounding_box();
      int x1 = get_top_left_x();
      int x2 = x1 + get_width();
      int y1 = get_top_left_y();
      int y2 = y1 + get_height();
      int jump_direction = 0;
      int jump_length = 0;
      bool jumped = false;

      const Rectangle &hero_center = hero.get_center_point();

      if (hero_center.get_y() < y1) {
        // fall to the north
        collision_box.set_y(y1 - 16);
        jump_direction = 2;
        jump_length = hero.get_top_left_y() + 16 - y1;
        jumped = try_jump(hero, collision_box, jump_direction, jump_length);
      }
      else if (hero_center.get_y() >= y2) {
        // fall to the south
        collision_box.set_y(y2);
        jump_direction = 6;
        jump_length = y2 - hero.get_top_left_y();
        jumped = try_jump(hero, collision_box, jump_direction, jump_length);
      }

      if (!jumped) {
        if (hero_center.get_x() >= x2) {
          // fall to the east
          collision_box.set_x(x2);
          jump_direction = 0;
          jump_length = x2 - hero.get_top_left_x();
          try_jump(hero, collision_box, jump_direction, jump_length);
        }
        else if (hero_center.get_x() < x1) {
          // fall to the west
          collision_box.set_x(x1 - 16);
          jump_direction = 4;
          jump_length = hero.get_top_left_x() + 16 - x1;
          try_jump(hero, collision_box, jump_direction, jump_length);
        }
      }
    }
  }
}
Пример #7
0
/**
 * \brief Draws the entity on the map.
 *
 * This is a redefinition of MapEntity::draw_on_map() to also draw the twinkling star
 * which has a special position.
 */
void Crystal::draw_on_map() {

  if (!is_drawn()) {
    return;
  }

  // draw the crystal
  MapEntity::draw_on_map();

  // draw the star
  if (is_drawn()) {
    get_map().draw_sprite(*star_sprite, get_top_left_x() + star_xy.get_x(), get_top_left_y() + star_xy.get_y());
  }
}
Пример #8
0
/**
 * \brief Draws the tile on the map.
 */
void DynamicTile::draw_on_map() {

  if (!is_drawn()) {
    return;
  }

  const Rectangle& camera_position = get_map().get_camera_position();
  Rectangle dst(0, 0);

  Rectangle dst_position(get_top_left_x() - camera_position.get_x(),
      get_top_left_y() - camera_position.get_y(),
      get_width(), get_height());

  tile_pattern.fill_surface(get_map().get_visible_surface(), dst_position,
      get_map().get_tileset(), camera_position);
}
Пример #9
0
/**
 * \brief Draws the tile on the specified surface.
 * \param dst_surface the destination surface
 * \param viewport coordinates of the top-left corner of dst_surface
 * relative to the map
 */
void Tile::draw(const SurfacePtr& dst_surface, const Point& viewport) {

  Rectangle dst_position(
      get_top_left_x() - viewport.x,
      get_top_left_y() - viewport.y,
      get_width(),
      get_height()
  );

  tile_pattern.fill_surface(
      dst_surface,
      dst_position,
      get_map().get_tileset(),
      viewport
  );
}
Пример #10
0
/**
 * \brief Draws the entity on the map.
 *
 * This is a redefinition of Entity::draw_on_map to repeat the block pattern.
 */
void CrystalBlock::draw_on_map() {

  const SpritePtr& sprite = get_sprite();
  if (sprite == nullptr) {
    return;
  }

  int x1 = get_top_left_x();
  int y1 = get_top_left_y();
  int x2 = x1 + get_width();
  int y2 = y1 + get_height();

  for (int y = y1; y < y2; y += 16) {
    for (int x = x1; x < x2; x += 16) {
      get_map().draw_visual(*sprite, x, y);
    }
  }
}
Пример #11
0
/**
 * \brief Draws the entity on the map.
 *
 * This is a redefinition of MapEntity::draw_on_map to repeat the block pattern.
 */
void CrystalBlock::draw_on_map() {

  if (!is_drawn()) {
    return;
  }

  Sprite& sprite = get_sprite();

  int x1 = get_top_left_x();
  int y1 = get_top_left_y();
  int x2 = x1 + get_width();
  int y2 = y1 + get_height();

  for (int y = y1; y < y2; y += 16) {
    for (int x = x1; x < x2; x += 16) {
      get_map().draw_sprite(sprite, x, y);
    }
  }
}
Пример #12
0
/**
 * \brief Draws the tile on the map.
 */
void DynamicTile::draw_on_map() {

  const CameraPtr& camera = get_map().get_camera();
  if (camera == nullptr) {
    return;
  }
  const Rectangle& camera_position = camera->get_bounding_box();

  Rectangle dst_position(get_top_left_x() - camera_position.get_x(),
      get_top_left_y() - camera_position.get_y(),
      get_width(), get_height());

  tile_pattern.fill_surface(
      get_map().get_camera_surface(),
      dst_position,
      get_map().get_tileset(),
      camera_position.get_xy()
  );
}
Пример #13
0
/**
 * \brief Returns whether lava is currently considered as obstacle by this entity.
 * \return true if lava is currently obstacle for this entity
 */
bool Enemy::is_lava_obstacle() const {

  if (obstacle_behavior == OBSTACLE_BEHAVIOR_FLYING) {
    return false;
  }

  if (is_being_hurt()) {
    return false;
  }

  const Layer layer = get_layer();
  const int x = get_top_left_x();
  const int y = get_top_left_y();
  if (get_map().get_ground(layer, x, y) == GROUND_LAVA
      || get_map().get_ground(layer, x + get_width() - 1, y) == GROUND_LAVA
      || get_map().get_ground(layer, x, y + get_height() - 1) == GROUND_LAVA
      || get_map().get_ground(layer, x + get_width() - 1, y + get_height() - 1) == GROUND_LAVA) {
    return false;
  }

  return true;
}
Пример #14
0
/**
 * \brief Updates the enemy.
 */
void Enemy::update() {

  MapEntity::update();

  if (is_suspended() || !is_enabled()) {
    return;
  }

  uint32_t now = System::now();

  if (being_hurt) {

    // see if we should stop the animation "hurt"
    if (now >= stop_hurt_date) {
      being_hurt = false;
      set_movement_events_enabled(true);

      if (life <= 0) {
        kill();
      }
      else if (is_immobilized()) {
        clear_movement();
        set_animation("immobilized");
        notify_immobilized();
      }
      else {
        clear_movement();
        restart();
      }
    }
  }

  if (life > 0 && invulnerable && now >= vulnerable_again_date && !being_hurt) {
    invulnerable = false;
  }

  if (life > 0 && !can_attack && !is_immobilized()
      && can_attack_again_date != 0 && now >= can_attack_again_date) {
    can_attack = true;
  }

  if (is_immobilized() && !is_killed() && now >= end_shaking_date &&
      get_sprite().get_current_animation() == "shaking") {

    restart();
  }

  if (is_immobilized() && !is_killed() && !is_being_hurt() && now >= start_shaking_date &&
      get_sprite().get_current_animation() != "shaking") {

    end_shaking_date = now + 2000;
    set_animation("shaking");
  }

  if (exploding) {
    uint32_t now = System::now();
    if (now >= next_explosion_date) {

      // create an explosion
      Rectangle xy;
      xy.set_x(get_top_left_x() + Random::get_number(get_width()));
      xy.set_y(get_top_left_y() + Random::get_number(get_height()));
      get_entities().add_entity(new Explosion("", LAYER_HIGH, xy, false));
      Sound::play("explosion");

      next_explosion_date = now + 200;
      nb_explosions++;

      if (nb_explosions >= 15) {
        exploding = false;
      }
    }
  }

  if (is_killed() && is_dying_animation_finished()) {

    // Create the pickable treasure if any.
    get_entities().add_entity(Pickable::create(get_game(),
        "", get_layer(), get_x(), get_y(),
        treasure, FALLING_HIGH, false));

    // Remove the enemy.
    remove_from_map();

    // Notify Lua that this enemy is dead.
    // We need to do this after remove_from_map() so that this enemy is
    // considered dead in functions like map:has_entities(prefix).
    notify_dead();
  }

  get_lua_context().enemy_on_update(*this);
}
Пример #15
0
/**
 * \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);
    }
  }
}