Example #1
0
/**
 * @brief Makes the door immediately open or closed.
 * @param door_open true to make it opened, false to make it closed.
 */
void Door::set_open(bool door_open) {

  state = door_open ? OPEN : CLOSED;

  if (door_open) {
    set_collision_modes(COLLISION_NONE); // to avoid being the hero's facing entity
  }
  else {
    get_sprite().set_current_animation("closed");
    set_collision_modes(COLLISION_FACING_POINT | COLLISION_SPRITE);

    // ensure that we are not closing the door on the hero
    if (is_on_map() && overlaps(get_hero())) {
      get_hero().avoid_collision(*this, (get_direction() + 2) % 4);
    }
  }

  if (is_on_map()) {
    update_dynamic_tiles();

    if (is_saved()) {
      get_savegame().set_boolean(savegame_variable, door_open);
    }

    if (door_open) {
      get_lua_context().door_on_opened(*this);
    }
    else {
      get_lua_context().door_on_closed(*this);
    }
  }
}
Example #2
0
/**
 * \brief This function is called when the enemy has just been hurt.
 * \param source the source of the attack
 * \param attack the attack that was just successful
 * \param life_points the number of life points lost by this enemy
 */
void Enemy::notify_hurt(MapEntity& source, EnemyAttack attack, int life_points) {

  get_lua_context().enemy_on_hurt(*this, attack, life_points);
  if (get_life() <= 0) {
    get_lua_context().enemy_on_dying(*this);
  }
}
Example #3
0
/**
 * \brief Gives the item to the player.
 */
void Pickable::try_give_item_to_player() {

  EquipmentItem& item = treasure.get_item();

  if (!can_be_picked
      || given_to_player
      || get_game().is_dialog_enabled()
      || !get_hero().can_pick_treasure(item)) {
    return;
  }

  given_to_player = true;

  remove_from_map();

  // play the sound
  const std::string& sound_id = item.get_sound_when_picked();
  if (!sound_id.empty()) {
    Sound::play(sound_id);
  }

  // give the item
  if (item.get_brandish_when_picked()) {
    // The treasure is brandished.
    // on_obtained() will be called after the dialog.
    get_hero().start_treasure(treasure, ScopedLuaRef());
  }
  else {
    treasure.give_to_player();

    // Call on_obtained() immediately since the treasure is not brandished.
    get_lua_context().item_on_obtained(item, treasure);
    get_lua_context().map_on_obtained_treasure(get_map(), treasure);
  }
}
Example #4
0
/**
 * \brief This function is called by the engine when there is a collision with another entity.
 *
 * If the entity is the hero, we allow him to interact with this entity.
 *
 * \param entity_overlapping the entity overlapping the detector
 * \param collision_mode the collision mode that detected the collision
 */
void Npc::notify_collision(Entity& entity_overlapping, CollisionMode collision_mode) {

  if (collision_mode == COLLISION_FACING && entity_overlapping.is_hero()) {

    Hero& hero = static_cast<Hero&>(entity_overlapping);

    if (get_commands_effects().get_action_key_effect() == CommandsEffects::ACTION_KEY_NONE
        && hero.is_free()) {

      if (subtype == USUAL_NPC // the hero can talk to usual NPCs from any direction
          || get_direction() == -1
          || hero.is_facing_direction4((get_direction() + 2) % 4)) {

        // show the appropriate action icon
        get_commands_effects().set_action_key_effect(subtype == USUAL_NPC ?
            CommandsEffects::ACTION_KEY_SPEAK : CommandsEffects::ACTION_KEY_LOOK);
      }
      else if (can_be_lifted() && get_equipment().has_ability(Ability::LIFT)) {
        get_commands_effects().set_action_key_effect(CommandsEffects::ACTION_KEY_LIFT);
      }
    }
  }
  else if (collision_mode == COLLISION_OVERLAPPING && entity_overlapping.get_type() == EntityType::FIRE) {

    if (behavior == BEHAVIOR_ITEM_SCRIPT) {
      EquipmentItem& item = get_equipment().get_item(item_name);
      get_lua_context()->item_on_npc_collision_fire(item, *this);
    }
    else {
      get_lua_context()->npc_on_collision_fire(*this);
    }
  }
}
Example #5
0
/**
 * \brief Draws the entity on the map.
 */
void Enemy::draw_on_map() {

  if (!is_drawn()) {
    return;
  }

  get_lua_context().enemy_on_pre_draw(*this);
  Detector::draw_on_map();
  get_lua_context().enemy_on_post_draw(*this);
}
Example #6
0
/**
 * \brief Notifies the appropriate script that the hero is interacting with this entity.
 */
void Npc::call_script_hero_interaction() {

  if (behavior == BEHAVIOR_MAP_SCRIPT) {
    get_lua_context()->entity_on_interaction(*this);
  }
  else {
    EquipmentItem& item = get_equipment().get_item(item_name);
    get_lua_context()->item_on_npc_interaction(item, *this);
  }
}
Example #7
0
/**
 * \copydoc MapEntity::notify_enabled
 */
void CustomEntity::notify_enabled(bool enabled) {

    Detector::notify_enabled(enabled);

    if (enabled) {
        get_lua_context().entity_on_enabled(*this);
    }
    else {
        get_lua_context().entity_on_disabled(*this);
    }
}
Example #8
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);
        }
      }
    }
  }
}
Example #9
0
/**
 * \copydoc Detector::notify_action_command_pressed
 */
bool Destructible::notify_action_command_pressed() {

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

  if ((effect == KeysEffect::ACTION_KEY_LIFT || effect == KeysEffect::ACTION_KEY_LOOK)
      && get_weight() != -1
      && !is_being_cut
      && !is_waiting_for_regeneration()
      && !is_regenerating) {

    if (get_equipment().has_ability(Ability::LIFT, get_weight())) {

      uint32_t explosion_date = get_can_explode() ? System::now() + 6000 : 0;
      get_hero().start_lifting(std::make_shared<CarriedItem>(
          get_hero(),
          *this,
          get_animation_set_id(),
          get_destruction_sound(),
          get_damage_on_enemies(),
          explosion_date)
      );

      // Play the sound.
      Sound::play("lift");

      // Create the pickable treasure.
      create_treasure();

      if (!get_can_regenerate()) {
        // Remove this destructible from the map.
        remove_from_map();
      }
      else {
        // The item can actually regenerate.
        play_destroy_animation();
      }

      // Notify Lua.
      get_lua_context().destructible_on_lifting(*this);
    }
    else {
      // Cannot lift the object.
      get_hero().start_grabbing();
      get_lua_context().destructible_on_looked(*this);
    }

    return true;
  }

  return false;
}
Example #10
0
/**
 * \brief Notifies this entity that it was just enabled or disabled.
 * \param enabled true if the entity is now enabled
 */
void Enemy::notify_enabled(bool enabled) {

  Detector::notify_enabled(enabled);

  if (enabled) {
    if (!initialized) {
      initialize();
    }
    restart();
    get_lua_context().enemy_on_enabled(*this);
  }
  else {
    get_lua_context().enemy_on_disabled(*this);
  }
}
Example #11
0
/**
 * \brief This function is called when the map is started and
 * the opening transition is finished.
 */
void Map::notify_opening_transition_finished() {

  visible_surface->set_opacity(255); // because the transition effect may have changed the opacity
  check_suspended();
  entities->notify_map_opening_transition_finished();
  get_lua_context().map_on_opening_transition_finished(*this, get_destination());
}
Example #12
0
/**
 * \brief Initializes the enemy.
 */
void Enemy::initialize() {

  if (!initialized) {
    initialized = true;
    get_lua_context().run_enemy(*this);
  }
}
Example #13
0
/**
 * @brief Implementation of sol.menu.stop().
 * @param l the Lua context that is calling this function
 * @return number of values to return to Lua
 */
int LuaContext::menu_api_stop(lua_State* l) {

  LuaContext& lua_context = get_lua_context(l);

  luaL_checktype(l, 1, LUA_TTABLE);

  int menu_ref = LUA_REFNIL;
  std::list<LuaMenuData>& menus = lua_context.menus;
  std::list<LuaMenuData>::iterator it;
  for (it = menus.begin(); it != menus.end(); it++) {
    int ref = it->ref;
    push_ref(l, ref);
    if (lua_equal(l, 1, -1)) {
      menu_ref = ref;
      lua_context.menu_on_finished(menu_ref);
      menus.erase(it);
      lua_context.destroy_ref(menu_ref);
      break;
    }
  }

  if (menu_ref == LUA_REFNIL) {
    push_string(l, "Unknown menu.");
    lua_error(l);
  }

  return 0;
}
Example #14
0
/**
 * \brief Notifies this entity that it was just enabled or disabled.
 * \param enabled true if the entity is now enabled
 */
void Enemy::notify_enabled(bool enabled) {

  Detector::notify_enabled(enabled);

  if (!is_on_map()) {
    return;
  }

  if (enabled) {
    restart();
    get_lua_context().entity_on_enabled(*this);
  }
  else {
    get_lua_context().entity_on_disabled(*this);
  }
}
Example #15
0
/**
 * \copydoc Detector::notify_collision(MapEntity&,CollisionMode)
 */
void CustomEntity::notify_collision(MapEntity& entity_overlapping, CollisionMode collision_mode) {

    if (collision_mode == COLLISION_FACING) {
        // This collision mode is only useful to set the facing entity, which
        // is already done by Detector.
        return;
    }

    // One or several collisions were detected with an another entity.
    // The collision tests could have been of any kind
    // (even a custom Lua collision test function),
    // except COLLISION_SPRITE that is handled separately.

    Debug::check_assertion(collision_mode == COLLISION_CUSTOM,
                           "Unexpected collision mode");

    // There is a collision: execute the callbacks.
    for (const CollisionInfo& info: successful_collision_tests) {
        get_lua_context().do_custom_entity_collision_callback(
            info.get_callback_ref(), *this, entity_overlapping
        );
    }

    successful_collision_tests.clear();
}
Example #16
0
/**
 * \brief Registers a Lua function to decide if this custom entity can traverse
 * other entities.
 *
 * This applies to entities that are not overridden by
 * set_can_traverse_entities(EntityType, bool)
 * or
 * set_can_traverse_entities(EntityType, const ScopedLuaRef&).
 *
 * \param traversable_test_ref Lua ref to a function that will do the test.
 */
void CustomEntity::set_can_traverse_entities(const ScopedLuaRef& traversable_test_ref) {

    can_traverse_entities_general = TraversableInfo(
                                        get_lua_context(),
                                        traversable_test_ref
                                    );
}
Example #17
0
/**
 * \brief Sets whether this custom entity can traverse other entities.
 *
 * This applies to entities that are not overridden by
 * set_can_traverse_entities(EntityType, bool)
 * or
 * set_can_traverse_entities(EntityType, const ScopedLuaRef&).
 *
 * \param traversable \c true to allow this entity to traverse other entities.
 */
void CustomEntity::set_can_traverse_entities(bool traversable) {

    can_traverse_entities_general = TraversableInfo(
                                        get_lua_context(),
                                        traversable
                                    );
}
Example #18
0
/**
 * @brief Sets a Lua function to be called when this destructible item is
 * destroyed.
 * @param destruction_callback_ref a Lua ref to the callback in the registry
 * (if you pass LUA_REFNIL, this function removes the previous callback that
 * was set, if any)
 */
void Destructible::set_destruction_callback(int destruction_callback_ref) {

  if (this->destruction_callback_ref != LUA_REFNIL) {
    get_lua_context().cancel_callback(this->destruction_callback_ref);
  }
  this->destruction_callback_ref = destruction_callback_ref;
}
Example #19
0
/**
 * \brief Suspends or resumes the movement and animations of the entities.
 *
 * This function is called when the game is being suspended
 * or resumed.
 *
 * \param suspended true to suspend the movement and the animations,
 * false to resume them
 */
void Map::set_suspended(bool suspended) {

  this->suspended = suspended;

  entities->set_suspended(suspended);
  get_lua_context().notify_map_suspended(*this, suspended);
}
Example #20
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() && !is_suspended()) {

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

      treasure_date = 0;
      treasure.ensure_obtainable();  // Make the chest empty if the treasure is not allowed.
      if (!treasure.is_empty()) {
        // Give a treasure to the player.

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

        if (treasure.is_saved()) {
          // Mark the treasure as found in the savegame.
          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, do nothing and unfreeze the hero.
          get_hero().start_free();
        }
      }
    }
  }

  MapEntity::update();
}
Example #21
0
/**
 * \copydoc MapEntity::notify_creating
 */
void CustomEntity::notify_creating() {

    Detector::notify_creating();

    ground_modifier = false;
    get_lua_context().run_custom_entity(*this);
}
Example #22
0
/**
 * \brief Implementation of sol.surface.create().
 * \param l the Lua context that is calling this function
 * \return number of values to return to Lua
 */
int LuaContext::surface_api_create(lua_State* l) {

    Surface* surface = NULL;
    if (lua_gettop(l) == 0) {
        // create an empty surface with the screen size
        surface = new Surface(VideoManager::get_instance()->get_quest_size());
    }
    else if (lua_type(l, 1) == LUA_TNUMBER) {
        // create an empty surface with the specified size
        int width = luaL_checkint(l, 1);
        int height = luaL_checkint(l, 2);
        surface = new Surface(width, height);
    }
    else if (lua_type(l, 1) == LUA_TSTRING) {
        // load from a file
        const std::string& file_name = lua_tostring(l, 1);
        bool language_specific = lua_toboolean(l, 2); // default is false
        surface = Surface::create_from_file(file_name, language_specific ?
                                            Surface::DIR_LANGUAGE : Surface::DIR_SPRITES);
    }
    else {
        luaL_typerror(l, 1, "number, string or no value");
    }

    if (surface == NULL) {
        // Image file not found or not valid.
        lua_pushnil(l);
    }
    else {
        get_lua_context(l).add_drawable(surface);
        push_surface(l, *surface);
    }
    return 1;
}
Example #23
0
/**
 * \brief Implementation of drawable:fade_out().
 * \param l The Lua context that is calling this function.
 * \return Number of values to return to Lua.
 */
int LuaContext::drawable_api_fade_out(lua_State* l) {

  uint32_t delay = 20;
  int callback_ref = LUA_REFNIL;

  Drawable& drawable = check_drawable(l, 1);

  if (lua_gettop(l) >= 2) {
    // the second argument can be the delay or the callback
    int index = 2;
    if (lua_isnumber(l, index)) {
      delay = lua_tonumber(l, index);
      index++;
    }
    // the next argument (if any) is the callback
    if (lua_gettop(l) >= index) {
      luaL_checktype(l, index, LUA_TFUNCTION);
      lua_settop(l, index);
      callback_ref = luaL_ref(l, LUA_REGISTRYINDEX);
    }
  }

  TransitionFade* transition = new TransitionFade(
      Transition::TRANSITION_CLOSING,
      drawable.get_transition_surface());
  transition->set_delay(delay);
  drawable.start_transition(*transition, callback_ref, &get_lua_context(l));

  return 0;
}
Example #24
0
/**
 * \brief Detects whether there were input events and if yes, handles them.
 */
void MainLoop::check_input() {

  // Check SDL events.
  std::unique_ptr<InputEvent> event = InputEvent::get_event();
  while (event != nullptr) {
    notify_input(*event);
    event = InputEvent::get_event();
  }

  // Check Lua requests.
  if (!lua_commands.empty()) {
    std::lock_guard<std::mutex> lock(lua_commands_mutex);
    for (const std::string& command : lua_commands) {
      std::cout << "\n";  // To make sure that the command delimiter starts on a new line.
      Logger::info("====== Begin Lua command #" + String::to_string(num_lua_commands_done) + " ======");
      const bool success = LuaTools::do_string(get_lua_context().get_internal_state(), command, "Lua command");
      if (success) {
        std::cout << "\n";
        Logger::info("====== End Lua command #" + String::to_string(num_lua_commands_done) + ": success ======");
      }
      else {
        std::cout << "\n";
        Logger::info("====== End Lua command #" + String::to_string(num_lua_commands_done) + ": error ======");
      }
      ++num_lua_commands_done;
    }
    lua_commands.clear();
  }
}
Example #25
0
/**
 * \brief Updates this entity.
 */
void Destructible::update() {

  MapEntity::update();

  if (is_suspended()) {
    return;
  }

  if (is_being_cut && get_sprite().is_animation_finished()) {

    if (!get_can_regenerate()) {
      // Remove this destructible from the map.
      remove_from_map();
    }
    else {
      is_being_cut = false;
      regeneration_date = System::now() + 10000;
    }
  }

  else if (is_waiting_for_regeneration()
      && System::now() >= regeneration_date
      && !overlaps(get_hero())) {
    get_sprite().set_current_animation("regenerating");
    is_regenerating = true;
    regeneration_date = 0;
    get_lua_context().destructible_on_regenerating(*this);
  }
  else if (is_regenerating && get_sprite().is_animation_finished()) {
    get_sprite().set_current_animation("on_ground");
    is_regenerating = false;
  }
}
Example #26
0
/**
 * \brief Notifies the game objects that the another map has just become active.
 */
void Game::notify_map_changed() {

  // Call game:on_map_changed() in Lua.
  get_lua_context().game_on_map_changed(*this, *current_map);

  // Notify the equipment.
  get_equipment().notify_map_changed(*current_map);
}
Example #27
0
/**
 * \brief Creates an explosion on this object.
 */
void Destructible::explode() {

  get_entities().add_entity(std::make_shared<Explosion>(
      "", get_layer(), get_xy(), true
  ));
  Sound::play("explosion");
  get_lua_context().destructible_on_exploded(*this);
}
Example #28
0
/**
 * \brief Notifies this enemy that it should restart his movement.
 *
 * This function is called when the enemy needs to restart its movement
 * because something happened (for example the enemy has just been created,
 * or it was just hurt).
 * By default, the "walking" animation is set on the enemy's sprites.
 */
void Enemy::restart() {

  if (is_immobilized()) {
    stop_immobilized();
  }
  set_animation("walking");
  get_lua_context().enemy_on_restarted(*this);
}
Example #29
0
/**
 * \brief Implementation of timer:stop().
 * \param l the Lua context that is calling this function
 * \return number of values to return to Lua
 */
int LuaContext::timer_api_stop(lua_State* l) {

  LuaContext& lua_context = get_lua_context(l);
  Timer& timer = check_timer(l, 1);
  lua_context.remove_timer(&timer);

  return 0;
}
Example #30
0
/**
 * \brief This function is called when this enemy detects a collision with another enemy.
 * \param other the other enemy
 * \param other_sprite the other enemy's sprite that overlaps a sprite of this enemy
 * \param this_sprite this enemy's sprite that overlaps the other
 */
void Enemy::notify_collision_with_enemy(Enemy& other,
    Sprite& other_sprite, Sprite& this_sprite) {

  if (is_in_normal_state()) {
    get_lua_context().enemy_on_collision_enemy(
        *this, other, other_sprite, this_sprite);
  }
}