Пример #1
0
/**
 * \copydoc Detector::notify_action_command_pressed
 */
bool ShopTreasure::notify_action_command_pressed() {

  if (get_hero().is_free()
      && get_keys_effect().get_action_key_effect() == KeysEffect::ACTION_KEY_LOOK) {

    LuaContext& lua_context = get_lua_context();
    lua_context.notify_shop_treasure_interaction(*this);
    return true;
  }

  return false;
}
Пример #2
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));
}
Пример #3
0
/**
 * \brief Starts this state.
 * \param previous_state The previous state.
 */
void Hero::HurtState::start(const HeroState* previous_state) {

  HeroState::start(previous_state);

  Equipment& equipment = get_equipment();

  Sound::play("hero_hurt");

  Hero& hero = get_hero();
  const uint32_t invincibility_duration = 2000;
  hero.set_invincible(true, invincibility_duration);
  get_sprites().set_animation_hurt();
  get_sprites().blink(invincibility_duration);

  if (has_source) {
    double angle = Geometry::get_angle(source_xy, hero.get_xy());
    std::shared_ptr<StraightMovement> movement =
        std::make_shared<StraightMovement>(false, true);
    movement->set_max_distance(24);
    movement->set_speed(120);
    movement->set_angle(angle);
    hero.set_movement(movement);
  }
  end_hurt_date = System::now() + 200;

  // See if the script customizes how the hero takes damages.
  bool handled = get_lua_context().hero_on_taking_damage(get_hero(), damage);

  if (!handled && damage != 0) {
    // No customized damage: perform the default calculation.
    // The level of the tunic reduces the damage,
    // but we remove at least 1 life point.
    int life_points = std::max(1, damage / (equipment.get_ability(Ability::TUNIC)));

    equipment.remove_life(life_points);
    if (equipment.has_ability(Ability::TUNIC)) {
      equipment.notify_ability_used(Ability::TUNIC);
    }
  }
}
Пример #4
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();
    }
  }
}
Пример #5
0
/**
 * @brief Updates the entity.
 */
void ShopItem::update() {

  if (is_looking_item && !get_game().is_dialog_enabled()) {

    // the description message has just finished
    const std::string question_dialog_id = "_shop.question";
    get_dialog_box().start_dialog(question_dialog_id);
    get_dialog_box().set_variable(question_dialog_id, price);
    is_asking_question = true;
    is_looking_item = false;
  }
  else if (is_asking_question && !get_game().is_dialog_enabled()) {

    // the question has just finished
    is_asking_question = false;
    int answer = get_dialog_box().get_last_answer();

    if (answer == 0) {

      // the player wants to buy the item
      Equipment& equipment = get_equipment();
      EquipmentItem& item = treasure.get_item();

      if (equipment.get_money() < price) {
        // not enough rupees
        Sound::play("wrong");
        get_dialog_box().start_dialog("_shop.not_enough_money");
      }
      else if (item.has_amount() && item.get_amount() >= item.get_max_amount()) {
        // the player already has the maximum amount of this item
        Sound::play("wrong");
        get_dialog_box().start_dialog("_shop.amount_full");
      }
      else {

        bool can_buy = get_lua_context().shop_item_on_buying(*this);
        if (can_buy) {

          // give the treasure
          equipment.remove_money(price);

          get_hero().start_treasure(treasure, LUA_REFNIL);
          if (treasure.is_saved()) {
            remove_from_map();
            get_savegame().set_boolean(treasure.get_savegame_variable(), true);
          }
          get_lua_context().shop_item_on_bought(*this);
        }
      }
    }
  }
}
Пример #6
0
/**
 * \brief Starts this state.
 * \param previous_state the previous state
 */
void Hero::BoomerangState::start(const HeroState* previous_state) {

  HeroState::start(previous_state);

  if (get_map().get_entities().is_boomerang_present()) {
    Hero& hero = get_hero();
    hero.set_state(new FreeState(hero));
  }
  else {
    get_sprites().set_animation_boomerang(tunic_preparing_animation);
    this->direction_pressed8 = get_commands().get_wanted_direction8();
  }
}
Пример #7
0
/**
 * \brief Updates this state.
 */
void Hero::HurtState::update() {

  State::update();

  Hero& hero = get_hero();
  if (hero.get_movement()->is_finished()
      || System::now() >= end_hurt_date) {
    // we have end_hurt_date because the movement may never finish if there is an obstacle

    hero.clear_movement();
    hero.start_state_from_ground();
  }
}
Пример #8
0
/**
 * \brief Draws this state.
 */
void Hero::TreasureState::draw_on_map() {

  State::draw_on_map();

  const Hero& hero = get_hero();
  int x = hero.get_x();
  int y = hero.get_y();

  const Rectangle &camera_position = get_map().get_camera_position();
  treasure.draw(get_map().get_visible_surface(),
      x - camera_position.get_x(),
      y - 24 - camera_position.get_y());
}
Пример #9
0
/**
 * @brief Updates the chest.
 *
 * This function is called repeatedly by the map.
 * This is a redefinition of MapEntity::update()
 * the handle the chest opening.
 */
void Chest::update() {

  if (is_open() && !suspended) {

    if (!treasure_given && treasure_date != 0 && System::now() >= treasure_date) {

      treasure_date = 0;

      if (!treasure.is_empty()) {
        // give a treasure to the player

        get_hero().start_treasure(treasure, LUA_REFNIL);
        treasure_given = true;
      }
      else { // the chest is empty

        // mark the treasure as found in the savegame
        if (treasure.is_saved()) {
          get_savegame().set_boolean(treasure.get_savegame_variable(), true);
        }

        treasure_given = true;

        bool done = get_lua_context().chest_on_empty(*this);
        if (!done) {

          // the script does not define any behavior:
          // by default, we tell the player the chest is empty
          Sound::play("wrong");
          get_dialog_box().start_dialog("_empty_chest");
          get_hero().start_free();
        }
      }
    }
  }

  MapEntity::update();
}
Пример #10
0
/**
 * @brief Updates the chest.
 *
 * This function is called repeatedly by the map.
 * This is a redefinition of MapEntity::update()
 * the handle the chest opening.
 */
void Chest::update() {

  if (is_open() && !suspended) {

    if (!treasure_given && treasure_date != 0 && System::now() >= treasure_date) {

      treasure_date = 0;

      if (treasure.get_item_name() != "_none") {
        // give a treasure to the player

        get_hero().start_treasure(treasure);
        treasure_given = true;
      }
      else { // the chest is empty

        // mark the treasure as found in the savegame
        int savegame_variable = treasure.get_savegame_variable();
        if (savegame_variable != -1) {
          get_savegame().set_boolean(savegame_variable, true);
        }

        treasure_given = true;

        if (!get_map_script().event_chest_empty(get_name())) {

          // the script does not define any behavior:
          // by default, we tell the player the chest is empty
          Sound::play("wrong");
          get_dialog_box().start_dialog("_empty_chest");
          get_hero().start_free();
        }
      }
    }
  }

  MapEntity::update();
}
Пример #11
0
/**
 * \brief Starts this state.
 * \param previous_state the previous state
 */
void Hero::LiftingState::start(const State* previous_state) {

  State::start(previous_state);

  // initialize the entity that will be lifted
  lifted_item->set_map(get_map());

  get_keys_effect().set_action_key_effect(KeysEffect::ACTION_KEY_THROW);
  get_sprites().set_animation_lifting();
  get_sprites().set_lifted_item(lifted_item);
  get_hero().set_facing_entity(nullptr);

  get_equipment().notify_ability_used(ABILITY_LIFT);
}
Пример #12
0
/**
 * \copydoc Detector::notify_action_command_pressed
 */
bool Crystal::notify_action_command_pressed() {

    if (get_hero().is_free() &&
            get_keys_effect().get_action_key_effect() == KeysEffect::ACTION_KEY_LOOK
       ) {
        get_keys_effect().set_action_key_effect(KeysEffect::ACTION_KEY_NONE);

        // start a dialog
        get_game().start_dialog("_crystal", ScopedLuaRef(), ScopedLuaRef());
        return true;
    }

    return false;
}
Пример #13
0
/**
 * \brief Updates this state.
 */
void Hero::HurtState::update() {

  HeroState::update();

  Hero& hero = get_hero();
  if ((hero.get_movement() != nullptr && hero.get_movement()->is_finished())
      || System::now() >= end_hurt_date) {
    // The movement may be finished, or the end date may be reached
    // when there is an obstacle or when there is no movement at all.

    hero.clear_movement();
    hero.start_state_from_ground();
  }
}
Пример #14
0
/**
 * \copydoc Detector::notify_action_command_pressed
 */
bool Bomb::notify_action_command_pressed() {

  KeysEffect::ActionKeyEffect effect = get_keys_effect().get_action_key_effect();

  if (effect == KeysEffect::ACTION_KEY_LIFT
      && get_hero().get_facing_entity() == this
      && get_hero().is_facing_point_in(get_bounding_box())) {

    get_hero().start_lifting(std::make_shared<CarriedItem>(
        get_hero(),
        *this,
        "entities/bomb",
        "",
        0,
        explosion_date)
    );
    Sound::play("lift");
    remove_from_map();
    return true;
  }

  return false;
}
Пример #15
0
/**
 * \brief Updates this state.
 */
void Hero::LiftingState::update() {

  State::update();

  lifted_item->update();

  if (!is_suspended() && !lifted_item->is_being_lifted()) { // the item has finished being lifted

    Hero& hero = get_hero();
    std::shared_ptr<CarriedItem> carried_item = lifted_item;
    lifted_item = nullptr; // we do not take care of the carried item from this state anymore
    hero.set_state(new CarryingState(hero, carried_item));
  }
}
Пример #16
0
/**
 * \brief Notifies this state that the hero has just failed to change its
 * position because of obstacles.
 */
void Hero::SwordLoadingState::notify_obstacle_reached() {

    PlayerMovementState::notify_obstacle_reached();

    Hero& hero = get_hero();
    Detector* facing_entity = hero.get_facing_entity();

    if (hero.is_facing_point_on_obstacle()     // he is really facing an obstacle
            && get_wanted_movement_direction8() == get_sprites().get_animation_direction8()   // he is trying to move towards the obstacle
            && (facing_entity == nullptr || !facing_entity->is_sword_ignored())) {               // the obstacle allows him to tap with his sword

        hero.set_state(new SwordTappingState(hero));
    }
}
Пример #17
0
/**
 * @brief Activates this sensor.
 *
 * This function is called when the hero overlaps the sensor.
 *
 * @param hero the hero
 */
void Sensor::activate(Hero& hero) {

  if (!activated_by_hero) {

    activated_by_hero = true;

    switch (subtype) {

      case CUSTOM:
        // we notify the scripts
        notifying_script = true;
        get_map_script().event_hero_on_sensor(get_name());
        notifying_script = false;
        get_hero().reset_movement();
        break;

      case CHANGE_LAYER:
        // we change the hero's layer
        get_entities().set_entity_layer(hero, get_layer());
        break;

      case RETURN_FROM_BAD_GROUND:
        // we indicate to the hero a location to return
        // after falling into a hole or some other ground
        get_hero().set_target_solid_ground_coords(get_xy(), get_layer());
        break;
    }
  }
  else {
    if (subtype == CUSTOM && !notifying_script && !get_game().is_suspended()) {
      notifying_script = true;
      get_map_script().event_hero_still_on_sensor(get_name());
      notifying_script = false;
    }
  }
}
Пример #18
0
/**
 *	check_heros_KK
 *	@val:	value to compare KK with
 *
 *	This function, like hero_check_KK_unused, is buggy!
 *	It does not check if the first slot is a valid hero.
 */
short check_heros_KK(short val) {

	Bit8u *hero;
	signed short sum;

	hero = get_hero(0);

	/* Orig-BUG: not checked if hero is valid */
	sum = host_readbs(hero + 0x47) + host_readbs(hero + 0x48);

	hero = get_hero(1);

	/* check class, group and dead status of hero in slot 2*/
	if (host_readb(hero + 0x21) && host_readb(hero + 0x87) == ds_readb(CURRENT_GROUP) && (!hero_dead(hero))) {
		sum += host_readbs(hero + 0x47) + host_readbs(hero + 0x48);
	}

#if !defined(__BORLANDC__)
	D1_INFO("Pruefe KK der ersten beiden Helden (%d) >= %d: ", sum, val);
	D1_INFO("%s\n", sum >= val ? "gelungen" : "mislungen");
#endif

	return (sum >= val) ? 1 : 0;
}
Пример #19
0
/**
 * \brief Updates this state.
 */
void Hero::PullingState::update() {

  State::update();

  Hero& hero = get_hero();
  if (!is_moving_grabbed_entity()) {

    int wanted_direction8 = get_commands().get_wanted_direction8();
    int opposite_direction8 = (get_sprites().get_animation_direction8() + 4) % 8;

    // stop pulling if the action key is released or if there is no more obstacle
    if (!get_commands().is_command_pressed(GameCommand::ACTION)
        || !hero.is_facing_obstacle()) {
      hero.set_state(new FreeState(hero));
    }

    // stop pulling the obstacle if the player changes his direction
    else if (wanted_direction8 != opposite_direction8) {
      hero.set_state(new GrabbingState(hero));
    }

    // see if the obstacle is an entity that the hero can pull
    else {

      Detector* facing_entity = hero.get_facing_entity();
      if (facing_entity != nullptr) {

        if (facing_entity->get_type() == EntityType::BLOCK) { // TODO use dynamic binding
          hero.try_snap_to_facing_entity();
        }

        if (facing_entity->start_movement_by_hero()) {

          std::string path = "  ";
          path[0] = path[1] = '0' + opposite_direction8;

          pulling_movement = std::make_shared<PathMovement>(
              path, 40, false, false, false
          );
          hero.set_movement(pulling_movement);
          pulled_entity = facing_entity;
          pulled_entity->notify_moving_by(hero);
        }
      }
    }
  }
}
Пример #20
0
/**
 * \brief Updates this state.
 */
void Hero::VictoryState::update() {

  HeroState::update();

  if (!finished && System::now() >= end_victory_date) {
    finished = true;
    if (!callback_ref.is_empty()) {
      // The behavior is defined by Lua.
      callback_ref.clear_and_call("hero victory callback");
    }
    else {
      // By default, get back to the normal state.
      Hero& hero = get_hero();
      hero.set_state(new FreeState(hero));
    }
  }
}
Пример #21
0
/**
 * \brief Starts this state.
 * \param previous_state The previous state.
 */
void Hero::UsingItemState::start(State* previous_state) {

  State::start(previous_state);

  bool interaction = false;
  Detector* facing_entity = get_hero().get_facing_entity();
  if (facing_entity != NULL && !facing_entity->is_being_removed()) {
    // Maybe the facing entity (e.g. an NPC) accepts an interaction with this
    // particular item.
    interaction = facing_entity->interaction_with_item(item_usage.get_item());
  }

  if (!interaction) {
    // no interaction occurred with the facing entity: use the item normally
    item_usage.start();
  }
}
Пример #22
0
/**
 * \brief Notifies this state that the hero has just failed to change its
 * position because of obstacles.
 */
void Hero::FreeState::notify_obstacle_reached() {

  PlayerMovementState::notify_obstacle_reached();

  Hero& hero = get_hero();
  if (hero.is_facing_point_on_obstacle()) { // he is really facing an obstacle

    uint32_t now = System::now();
    if (pushing_direction4 == -1) {
      start_pushing_date = now + 800; // start animation "pushing" after 800 ms
      pushing_direction4 = hero.get_animation_direction();
    }
    else if (now >= start_pushing_date) {
      hero.set_state(new PushingState(hero));
    }
  }
}
Пример #23
0
/**
 * \brief Starts this state.
 * \param previous_state the previous state
 */
void Hero::StairsState::start(const State* previous_state) {

  State::start(previous_state);

  // movement
  int speed = stairs.is_inside_floor() ? 40 : 24;
  std::string path = stairs.get_path(way);
  std::shared_ptr<PathMovement> movement = std::make_shared<PathMovement>(
      path, speed, false, true, false
  );

  // sprites and sound
  HeroSprites& sprites = get_sprites();
  if (carried_item == nullptr) {
    sprites.set_animation_walking_normal();
  }
  else {
    sprites.set_lifted_item(carried_item);
    sprites.set_animation_walking_carrying();
  }
  sprites.set_animation_direction((path[0] - '0') / 2);
  get_keys_effect().set_action_key_effect(KeysEffect::ACTION_KEY_NONE);

  Hero& hero = get_hero();
  if (stairs.is_inside_floor()) {
    if (way == Stairs::NORMAL_WAY) {
      // Towards an upper layer: change the layer now
      Layer layer = stairs.get_layer();
      Debug::check_assertion(layer != LAYER_HIGH, "Invalid stairs layer");
      get_entities().set_entity_layer(hero, Layer(layer + 1));
    }
  }
  else {
    sprites.set_clipping_rectangle(stairs.get_clipping_rectangle(way));
    if (way == Stairs::REVERSE_WAY) {
      Point dxy = movement->get_xy_change();
      int fix_y = 8;
      if (path[path.size() - 1] == '2') {
        fix_y *= -1;
      }
      hero.set_xy(hero.get_x() - dxy.x, hero.get_y() - dxy.y + fix_y);
    }
  }
  hero.set_movement(movement);
}
Пример #24
0
/**
 * \brief Updates this state.
 */
void Hero::SpinAttackState::update() {

  // check the animation
  Hero& hero = get_hero();
  if (get_sprites().is_animation_finished()) {
    hero.set_state(new FreeState(hero));
  }

  // check the movement if any
  if (hero.get_movement() != NULL && hero.get_movement()->is_finished()) {
    hero.clear_movement();

    if (!being_pushed) {
      // end of a super spin attack
      hero.set_state(new FreeState(hero));
    }
  }
}
Пример #25
0
/**
 * \brief Updates this state.
 */
void Hero::SwordTappingState::update() {

  State::update();

  Hero& hero = get_hero();
  if (hero.get_movement() == NULL) {
    // the hero is not being pushed after hitting an enemy

    const Rectangle& facing_point = hero.get_facing_point();

    if (!get_commands().is_command_pressed(GameCommands::ATTACK)
        || get_commands().get_wanted_direction8() != get_sprites().get_animation_direction8()
        || !get_map().test_collision_with_obstacles(hero.get_layer(), facing_point.get_x(), facing_point.get_y(), hero)) {
      // the sword key has been released, the player has moved or the obstacle is gone

      if (get_sprites().get_current_frame() >= 5) {
        // when the animation is ok, stop tapping the wall, go back to loading the sword
        hero.set_state(new SwordLoadingState(hero));
      }
    }
    else {

      // play the sound every 100 ms
      uint32_t now = System::now();
      if (get_sprites().get_current_frame() == 3 && now >= next_sound_date) {

        Detector* facing_entity = hero.get_facing_entity();
        std::string sound_id;
        if (facing_entity != NULL) {
          sound_id = facing_entity->get_sword_tapping_sound();
        }
        else {
          sound_id = "sword_tapping";
        }
        Sound::play(sound_id);
        next_sound_date = now + 100;
      }
    }
  }
  else if (hero.get_movement()->is_finished()) {
    // the hero was pushed by an enemy
    hero.set_state(new FreeState(hero));
  }
}
Пример #26
0
int			run_epikong()
{
  t_map			map;
  char			*path;

  if ((path = init_screen()) == NULL)
    return (EXIT_FAILURE);
  if (start_music() == EXIT_FAILURE)
    return (EXIT_FAILURE);
  if (start_parsing(path, &map) == EXIT_FAILURE)
    return (EXIT_FAILURE);
  if (get_monster(&map) == EXIT_FAILURE)
    return (EXIT_FAILURE);
  if (get_hero(&map) == EXIT_FAILURE)
    return (EXIT_FAILURE);
  if (parse_map(&map) == EXIT_FAILURE)
    return (EXIT_FAILURE);
  return (EXIT_SUCCESS);
}
Пример #27
0
/**
 * \brief Notifies this state that the hero has just changed its
 * position.
 */
void Hero::PullingState::notify_position_changed() {

  if (is_moving_grabbed_entity()) {
    // if the entity has made more than 8 pixels and is aligned on the grid,
    // we stop the movement

    PathMovement* movement = static_cast<PathMovement*>(get_hero().get_movement());

    bool horizontal = get_sprites().get_animation_direction() % 2 == 0;
    bool has_reached_grid = movement->get_total_distance_covered() >= 16
      && ((horizontal && pulled_entity->is_aligned_to_grid_x())
          || (!horizontal && pulled_entity->is_aligned_to_grid_y()));

    if (has_reached_grid) {
      pulled_entity->update();
      stop_moving_pulled_entity();
    }
  }
}
Пример #28
0
/**
 * \brief Notifies this state that the hero has just attacked an enemy.
 * \param attack the attack
 * \param victim the enemy just hurt
 * \param result indicates how the enemy has reacted to the attack (see Enemy.h)
 * \param killed indicates that the attack has just killed the enemy
 */
void Hero::SwordTappingState::notify_attacked_enemy(EnemyAttack attack, Enemy& victim,
    EnemyReaction::Reaction& result, bool killed) {

  if (result.type != EnemyReaction::IGNORED && attack == ATTACK_SWORD) {

    if (victim.get_push_hero_on_sword()) {

      Hero& hero = get_hero();
      double angle = Geometry::get_angle(victim.get_x(), victim.get_y(),
          hero.get_x(), hero.get_y());
      StraightMovement* movement = new StraightMovement(false, true);
      movement->set_max_distance(24);
      movement->set_speed(120);
      movement->set_angle(angle);
      hero.set_movement(movement);
      get_sprites().set_animation_walking_normal();
    }
  }
}
Пример #29
0
/**
 * \brief Notifies this state that the action command was just pressed.
 */
void Hero::FreeState::notify_action_command_pressed() {

  Hero& hero = get_hero();
  if (get_keys_effect().is_action_key_acting_on_facing_entity()) {

    // action on the facing entity
    hero.get_facing_entity()->notify_action_command_pressed();
  }
  else if (hero.is_facing_point_on_obstacle()) {

    // grab an obstacle
    hero.set_state(new GrabbingState(hero));
  }
  else if (get_equipment().has_ability("run")) {

    // run
    hero.start_running();
  }
}
Пример #30
0
/**
 * \brief Makes the hero stop pulling the entity he is facing.
 *
 * This function is called while moving the entity, when the
 * hero or the entity collides with an obstacle or when
 * the hero's movement is finished.
 */
void Hero::PullingState::stop_moving_pulled_entity() {

  Hero& hero = get_hero();
  if (pulled_entity != nullptr) {
    pulled_entity->stop_movement_by_hero();

    // the hero may have moved one or several pixels too much
    // because he moved before the block, not knowing that the block would not follow him

    int direction4 = get_sprites().get_animation_direction();
    switch (direction4) {

      case 0:
        // east
        hero.set_x(pulled_entity->get_x() - 16);
        break;

      case 1:
        // north
        hero.set_y(pulled_entity->get_y() + 16);
        break;

      case 2:
        // west
        hero.set_x(pulled_entity->get_x() + 16);
        break;

      case 3:
        // south
        hero.set_y(pulled_entity->get_y() - 16);
        break;
    }

    hero.clear_movement();
    pulling_movement = nullptr;
    MapEntity* entity_just_moved = pulled_entity;
    pulled_entity = nullptr;
    entity_just_moved->notify_moved_by(hero);
  }

  hero.set_state(new GrabbingState(hero));
}