Esempio n. 1
0
/**
 * \brief Throws the item.
 * \param direction direction where the hero throws the item (0 to 3)
 */
void CarriedItem::throw_item(int direction) {

  this->throwing_direction = direction;
  this->is_lifting = false;
  this->is_throwing = true;

  // play the sound
  Sound::play("throw");

  // stop the sprite animation
  Sprite& sprite = get_sprite();
  sprite.set_current_animation("stopped");

  // set the movement of the item sprite
  set_y(hero.get_y());
  std::shared_ptr<StraightMovement> movement =
      std::make_shared<StraightMovement>(false, false);
  movement->set_speed(200);
  movement->set_angle(Geometry::degrees_to_radians(direction * 90));
  clear_movement();
  set_movement(movement);

  this->y_increment = -2;
  this->next_down_date = System::now() + 40;
  this->item_height = 18;
}
Esempio n. 2
0
/**
 * \brief This function is called by the engine when an entity overlaps the pickable item.
 *
 * If the entity is the player, we give him the item, and the map is notified
 * to destroy it.
 * \param entity_overlapping the entity overlapping the detector
 * \param collision_mode the collision mode that detected the collision
 */
void Pickable::notify_collision(MapEntity& entity_overlapping, CollisionMode collision_mode) {

    if (entity_overlapping.is_hero()) {
        try_give_item_to_player();
    }
    else if (entity_followed == NULL) {

        if (entity_overlapping.get_type() == ENTITY_BOOMERANG) {
            Boomerang& boomerang = static_cast<Boomerang&>(entity_overlapping);
            if (!boomerang.is_going_back()) {
                boomerang.go_back();
            }
            entity_followed = &boomerang;
        }
        else if (entity_overlapping.get_type() == ENTITY_HOOKSHOT) {
            Hookshot& hookshot = static_cast<Hookshot&>(entity_overlapping);
            if (!hookshot.is_going_back()) {
                hookshot.go_back();
            }
            entity_followed = &hookshot;
        }

        if (entity_followed != NULL) {
            clear_movement();
            set_movement(new FollowMovement(entity_followed, 0, 0, true));
            falling_height = FALLING_NONE;
            set_blinking(false);
        }
    }
}
Esempio n. 3
0
/**
 * @brief Updates this entity.
 */
void Hookshot::update() {

  MapEntity::update();

  if (suspended) {
    return;
  }

  uint32_t now = System::now();
  if (now >= next_sound_date) {
    Sound::play("hookshot");
    next_sound_date = now + 150;
  }

  if (entity_reached == NULL) {
    if (!going_back) {

      if (has_to_go_back) {
        going_back = true;
        Movement *movement = new TargetMovement(&get_hero(), 192, true);
        clear_movement();
        set_movement(movement);
      }
      else if (get_distance(get_hero()) >= 120) {
        go_back();
      }
    }
    else if (get_distance(get_hero()) == 0 ||
        (get_movement() != NULL && get_movement()->is_finished())) {
      remove_from_map();
      get_hero().start_state_from_ground();
    }
  }
}
Esempio n. 4
0
/**
 * \brief This function is called by the engine when an entity overlaps the pickable item.
 *
 * If the entity is the player, we give him the item, and the map is notified
 * to destroy it.
 * \param entity_overlapping the entity overlapping the detector
 * \param collision_mode the collision mode that detected the collision
 */
void Pickable::notify_collision(MapEntity& entity_overlapping, CollisionMode /* collision_mode */) {

  if (entity_overlapping.is_hero()) {
    try_give_item_to_player();
  }
  else if (entity_followed == nullptr) {

    MapEntityPtr shared_entity_overlapping =
        std::static_pointer_cast<MapEntity>(entity_overlapping.shared_from_this());
    if (entity_overlapping.get_type() == ENTITY_BOOMERANG) {
      Boomerang& boomerang = static_cast<Boomerang&>(entity_overlapping);
      if (!boomerang.is_going_back()) {
        boomerang.go_back();
      }
      entity_followed = shared_entity_overlapping;
    }
    else if (entity_overlapping.get_type() == ENTITY_HOOKSHOT) {
      Hookshot& hookshot = static_cast<Hookshot&>(entity_overlapping);
      if (!hookshot.is_going_back()) {
        hookshot.go_back();
      }
      entity_followed = shared_entity_overlapping;
    }

    if (entity_followed != nullptr) {
      clear_movement();
      set_movement(std::make_shared<FollowMovement>(
          entity_followed, 0, 0, true
      ));
      falling_height = FALLING_NONE;
      set_blinking(false);
    }
  }
}
Esempio n. 5
0
/**
 * \brief Hurts the enemy.
 *
 * Updates its state, its sprite and plays the sound.
 *
 * \param source the entity attacking the enemy (often the hero)
 */
void Enemy::hurt(MapEntity& source) {

  uint32_t now = System::now();

  // update the enemy state
  being_hurt = true;
  set_movement_events_enabled(false);

  can_attack = false;
  can_attack_again_date = now + 300;

  // graphics and sounds
  set_animation("hurt");
  play_hurt_sound();

  // stop any movement
  clear_movement();

  // push the enemy back
  if (pushed_back_when_hurt) {
    double angle = source.get_angle(*this);
    StraightMovement* movement = new StraightMovement(false, true);
    movement->set_max_distance(24);
    movement->set_speed(120);
    movement->set_angle(angle);
    set_movement(movement);
  }

  stop_hurt_date = now + 300;
}
Esempio n. 6
0
/**
 * \brief This function is called repeatedly.
 */
void Bomb::update() {

  Detector::update();

  if (is_suspended()) {
    return;
  }

  // check the explosion date
  uint32_t now = System::now();
  if (now >= explosion_date) {
    explode();
  }
  else if (now >= explosion_date - 1500
      && get_sprite().get_current_animation() != "stopped_explosion_soon") {
    get_sprite().set_current_animation("stopped_explosion_soon");
  }

  // destroy the movement once finished
  if (get_movement() != nullptr && get_movement()->is_finished()) {
    clear_movement();
  }

  // check collision with explosions, streams, etc.
  check_collision_with_detectors();
}
Esempio n. 7
0
/**
 * \brief Kills the enemy.
 *
 * This function is called when the enemy has no more health points.
 */
void Enemy::kill() {

  // if the enemy is immobilized, give some money
  if (rank == RANK_NORMAL && is_immobilized() && !treasure.is_saved()) {
    // TODO choose random money (can we do this from scripts?)
  }

  // stop any movement and disable attacks
  set_collision_modes(COLLISION_NONE);
  clear_movement();
  invulnerable = true;
  can_attack = false;
  can_attack_again_date = 0;
  dying_animation_started = true;

  if (hurt_style == HURT_BOSS) {
    // A boss: create some explosions.
    exploding = true;
    nb_explosions = 0;
    next_explosion_date = System::now() + 2000;
  }
  else {
    // Replace the enemy sprites.
    clear_sprites();
    switch (get_ground_below()) {

      case GROUND_HOLE:
        // TODO animation of falling into a hole.
        Sound::play("jump");
        break;

      case GROUND_DEEP_WATER:
        // TODO water animation.
        Sound::play("splash");
        break;

      case GROUND_LAVA:
        // TODO lava animation.
        Sound::play("splash");
        break;

      default:
        create_sprite("enemies/enemy_killed");
        Sound::play("enemy_killed");
        break;
    }
  }

  // save the enemy state if required
  if (is_saved()) {
    get_savegame().set_boolean(savegame_variable, true);
  }
}
Esempio n. 8
0
/**
 * @brief Attachs the hookshot to an entity and makes the hero move towards this entity.
 * @param entity_reached the entity to attach the hookshot to
 */
void Hookshot::attach_to(MapEntity& entity_reached) {

  Debug::check_assertion(this->entity_reached == NULL,
      "The hookshot is already attached to an entity");

  this->entity_reached = &entity_reached;
  clear_movement();
  int direction = get_sprite().get_current_direction();
  std::string path = " ";
  path[0] = '0' + (direction * 2);
  get_hero().set_movement(new PathMovement(path, 192, true, false, false));
}
Esempio n. 9
0
/**
 * @brief Resets the block at its initial position.
 */
void Block::reset() {

  if (get_movement() != NULL) {
    // the block was being pushed or pulled by the hero
    clear_movement();
    when_can_move = System::now() + moving_delay;
  }

  set_xy(initial_position);
  last_position.set_xy(initial_position);
  this->maximum_moves = initial_maximum_moves;
}
Esempio n. 10
0
/**
 * \brief Resets the block at its initial position.
 */
void Block::reset() {

  if (get_movement() != nullptr) {
    // the block was being pushed or pulled by the hero
    clear_movement();
    when_can_move = System::now() + moving_delay;
  }

  last_position = initial_position;
  this->maximum_moves = initial_maximum_moves;
  set_xy(initial_position);
  notify_position_changed();
}
Esempio n. 11
0
/**
 * \brief This function is called when this entity stops being moved by the
 * hero.
 */
void Block::stop_movement_by_hero() {

  clear_movement();
  when_can_move = System::now() + moving_delay;

  // see if the block has moved
  if (get_xy() != last_position) {

    // the block has moved
    last_position = get_xy(); // save the new position for next time

    if (maximum_moves == 1) { // if the block could be moved only once,
      maximum_moves = 0;      // then it cannot move anymore
    }
  }
}
Esempio n. 12
0
/**
 * @brief This function is called when this entity stops being moved by the
 * hero.
 */
void Block::stop_movement_by_hero() {

  clear_movement();
  when_can_move = System::now() + moving_delay;

  // see if the block has moved
  if (get_x() != last_position.get_x() || get_y() != last_position.get_y()) {

    // the block has moved
    last_position.set_xy(get_x(), get_y()); // save the new position for next time

    if (maximum_moves == 1) { // if the block could be moved only once,
      maximum_moves = 0;      // then it cannot move anymore
    }
  }

  // notify the script
  get_map_script().event_block_moved(get_name());
}
Esempio n. 13
0
/**
 * \brief This function is called when a conveyor belt detects a collision with this entity.
 * \param conveyor_belt a conveyor belt
 * \param dx direction of the x move in pixels (0, 1 or -1)
 * \param dy direction of the y move in pixels (0, 1 or -1)
 */
void Bomb::notify_collision_with_conveyor_belt(ConveyorBelt& conveyor_belt, int dx, int dy) {

  if (get_movement() == NULL) {

    // check that a significant part of the bomb is on the conveyor belt
    Rectangle center = get_center_point();
    center.add_xy(-1, -1);
    center.set_size(2, 2);

    if (conveyor_belt.overlaps(center)) {
      set_xy(conveyor_belt.get_xy());

      std::string path = "  ";
      path[0] = path[1] = '0' + conveyor_belt.get_direction();
      clear_movement();
      set_movement(new PathMovement(path, 64, false, false, false));
    }
  }
}
Esempio n. 14
0
/**
 * \copydoc MapEntity::notify_collision_with_stream
 */
void Bomb::notify_collision_with_stream(Stream& stream, int /* dx */, int /* dy */) {

  if (get_movement() == nullptr) {
    // TODO use a StreamAction, since it now works with any entity and not only the hero.

    // Check that a significant part of the bomb is on the stream.
    Rectangle center(get_center_point(), Size(2, 2));
    center.add_xy(-1, -1);

    if (stream.overlaps(center)) {
      set_xy(stream.get_xy());

      std::string path = "  ";
      path[0] = path[1] = '0' + stream.get_direction();
      clear_movement();
      set_movement(std::make_shared<PathMovement>(
          path, 64, false, false, false
      ));
    }
  }
}
Esempio n. 15
0
/**
 * \brief Updates the boomerang.
 */
void Boomerang::update() {

  MapEntity::update();

  if (is_suspended()) {
    return;
  }

  uint32_t now = System::now();
  if (now >= next_sound_date) {
    Sound::play("boomerang");
    next_sound_date = now + 150;
  }

  if (!going_back && has_to_go_back) {
    going_back = true;
    clear_movement();
    set_movement(new TargetMovement(&hero, 0, 0, speed, true));
    get_entities().set_entity_layer(*this, hero.get_layer()); // because the hero's layer may have changed
  }
}
Esempio n. 16
0
/**
 * \brief Updates this entity.
 */
void Hookshot::update() {

  MapEntity::update();

  if (is_suspended()) {
    return;
  }

  uint32_t now = System::now();
  if (now >= next_sound_date) {
    Sound::play("hookshot");
    next_sound_date = now + 150;
  }

  if (entity_reached == nullptr) {
    if (!going_back) {

      if (has_to_go_back) {
        going_back = true;
        std::shared_ptr<Movement> movement = std::make_shared<TargetMovement>(
            std::static_pointer_cast<Hero>(get_hero().shared_from_this()),
            0,
            0,
            192,
            true
        );
        clear_movement();
        set_movement(movement);
      }
      else if (get_distance(get_hero()) >= 120) {
        go_back();
      }
    }
    else if (get_distance(get_hero()) == 0 ||
        (get_movement() != nullptr && get_movement()->is_finished())) {
      remove_from_map();
      get_hero().start_state_from_ground();
    }
  }
}
Esempio n. 17
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);
}
Esempio n. 18
0
/**
 * \brief This function is called repeatedly.
 */
void CarriedItem::update() {

  // update the sprite and the position
  Entity::update();

  if (is_suspended()) {
    return;
  }

  // when the hero finishes lifting the item, start carrying it
  if (is_lifting && get_movement()->is_finished()) {
    is_lifting = false;

    // make the item follow the hero
    clear_movement();
    set_movement(std::make_shared<RelativeMovement>(
        std::static_pointer_cast<Hero>(hero.shared_from_this()),
        0,
        -18,
        true
    ));
  }

  // when the item has finished flying, destroy it
  else if (can_explode() && !is_breaking) {

    uint32_t now = System::now();

    if (now >= explosion_date) {
      break_item();
    }
    else if (will_explode_soon()) {

      std::string animation = get_sprite().get_current_animation();
      if (animation == "stopped") {
        get_sprite().set_current_animation("stopped_explosion_soon");
      }
      else if (animation == "walking") {
        get_sprite().set_current_animation("walking_explosion_soon");
      }
    }
  }

  if (is_broken()) {
    remove_from_map();
  }
  else if (is_throwing) {
    shadow_sprite->update();

    if (break_one_layer_above) {
      break_item();
      int layer = get_layer();
      if (layer != get_map().get_highest_layer()) {
        get_entities().set_entity_layer(*this, layer + 1);
      }
      break_one_layer_above = false;
    }
    else if (get_movement()->is_stopped() || y_increment >= 7) {
      // Interrupt the movement.
      break_item_on_ground();
    }
    else {
      uint32_t now = System::now();
      while (now >= next_down_date) {
        next_down_date += 40;
        item_height -= y_increment;
        y_increment++;
      }
    }
  }
}
Esempio n. 19
0
/**
 * \brief Updates this entity.
 */
void Arrow::update() {

  MapEntity::update();

  if (is_suspended()) {
    return;
  }

  uint32_t now = System::now();

  // stop the movement if necessary (i.e. stop() was called)
  if (stop_now) {
    clear_movement();
    stop_now = false;

    if (entity_reached != NULL) {
      // the arrow just hit an entity (typically an enemy) and this entity may have a movement
      Rectangle dxy(get_x() - entity_reached->get_x(), get_y() - entity_reached->get_y());
      set_movement(new FollowMovement(entity_reached, dxy.get_x(), dxy.get_y(), true));
    }
  }

  if (entity_reached != NULL) {

    // see if the entity reached is still valid
    if (is_stopped()) {
      // the arrow is stopped because the entity that was reached just disappeared
      disappear_date = now;
    }
    else if (entity_reached->get_type() == ENTITY_DESTRUCTIBLE && !entity_reached->is_obstacle_for(*this)) {
      disappear_date = now;
    }
    else if (entity_reached->get_type() == ENTITY_ENEMY && ((Enemy*) entity_reached)->is_dying()) {
      // the enemy is dying
      disappear_date = now;
    }
  }

  // see if the arrow just hit a wall or an entity
  bool reached_obstacle = false;

  if (get_sprite().get_current_animation() != "reached_obstacle") {

    if (entity_reached != NULL) {
      // the arrow was just attached to an entity
      reached_obstacle = true;
    }
    else if (is_stopped()) {

      if (has_reached_map_border()) {
        // the map border was reached: destroy the arrow
        disappear_date = now;
      }
      else {
        // the arrow has just hit another obstacle
        reached_obstacle = true;
      }
    }
  }

  if (reached_obstacle) {
    // an obstacle or an entity was just reached
    disappear_date = now + 1500;
    get_sprite().set_current_animation("reached_obstacle");
    Sound::play("arrow_hit");

    if (entity_reached == NULL) {
      clear_movement();
    }
    check_collision_with_detectors(false);
  }

  // destroy the arrow when disappear_date is reached
  if (now >= disappear_date) {
    remove_from_map();
  }
}
Esempio n. 20
0
MapEntity::~MapEntity() {
	clear_movement();
	clear_old_movements();
	clear_sprites();
	clear_old_sprites();
}
Esempio n. 21
0
/**
 * @brief This function is called repeatedly.
 */
void CarriedItem::update() {

  // update the sprite and the position
  MapEntity::update();

  if (suspended) {
    return;
  }

  // when the hero finishes lifting the item, start carrying it
  if (is_lifting && get_movement()->is_finished()) {
    is_lifting = false;

    // make the item follow the hero
    clear_movement();
    set_movement(new FollowMovement(&hero, 0, -18, true));
  }

  // when the item has finished flying, destroy it
  else if (can_explode() && !is_breaking) {
    
    uint32_t now = System::now();
    
    if (now >= explosion_date) {
      break_item();
    }
    else if (will_explode_soon()) {

      std::string animation = get_sprite().get_current_animation();
      if (animation == "stopped") {
        get_sprite().set_current_animation("stopped_explosion_soon");
      }
      else if (animation == "walking") {
        get_sprite().set_current_animation("walking_explosion_soon");
      }
    }
  }

  if (is_throwing) {
    shadow_sprite->update();

    if (is_broken()) {
      remove_from_map();
    }
    else if (break_on_intermediate_layer) {
      break_item();
      get_entities().set_entity_layer(*this, LAYER_INTERMEDIATE);
      break_on_intermediate_layer = false;
    }
    else if (get_movement()->is_stopped() || y_increment >= 7) {
      break_item();
    }
    else {
      uint32_t now = System::now();
      while (now >= next_down_date) {
        next_down_date += 40;
        item_height -= y_increment;
        y_increment++;
      }
    }
  }
}