Пример #1
0
/**
 * \brief Creates a collision test info.
 * \param lua_context The Lua context.
 * \param collision_test_ref Lua ref to a custom collision test.
 * \param callback_ref Lua ref to a function to call when this collision is
 * detected.
 */
CustomEntity::CollisionInfo::CollisionInfo(
    LuaContext& lua_context,
    const ScopedLuaRef& custom_test_ref,
    const ScopedLuaRef& callback_ref
):
    lua_context(&lua_context),
    built_in_test(COLLISION_CUSTOM),
    custom_test_ref(custom_test_ref),
    callback_ref(callback_ref) {

    Debug::check_assertion(!callback_ref.is_empty(), "Missing callback ref");
}
Пример #2
0
/**
 * \brief Registers a function to be called when the specified test detects a
 * collision.
 * \param collision_test_ref Lua ref to a custom collision test.
 * \param callback_ref Lua ref to a function to call when this collision is
 * detected.
 */
void CustomEntity::add_collision_test(
    const ScopedLuaRef& collision_test_ref,
    const ScopedLuaRef& callback_ref
) {
    Debug::check_assertion(!callback_ref.is_empty(), "Missing collision callback");

    add_collision_mode(COLLISION_CUSTOM);

    collision_tests.emplace_back(
        get_lua_context(),
        collision_test_ref,
        callback_ref
    );
}
Пример #3
0
/**
 * \brief Creates a new music.
 * \param music_id Id of the music (file name without extension).
 * \param loop Whether the music should loop when reaching its end.
 * \param callback_ref Lua function to call when the music ends
 * or an empty ref.
 * There cannot be both a loop and a callback at the same time.
 */
Music::Music(
    const std::string& music_id,
    bool loop,
    const ScopedLuaRef& callback_ref):
  id(music_id),
  format(OGG),
  loop(loop),
  callback_ref(callback_ref),
  source(AL_NONE) {

  Debug::check_assertion(!loop || callback_ref.is_empty(),
      "Attempt to set both a loop and a callback to music"
  );

  for (int i = 0; i < nb_buffers; i++) {
    buffers[i] = AL_NONE;
  }
}
Пример #4
0
/**
 * \brief Registers a function to be called when the specified test detects a
 * collision.
 * \param collision_test A built-in collision test.
 * \param callback_ref Lua ref to a function to call when this collision is
 * detected.
 */
void CustomEntity::add_collision_test(
    CollisionMode collision_test,
    const ScopedLuaRef& callback_ref
) {
    Debug::check_assertion(collision_test != COLLISION_NONE, "Invalid collision mode");
    Debug::check_assertion(!callback_ref.is_empty(), "Missing collision callback");

    if (collision_test == COLLISION_SPRITE) {
        add_collision_mode(COLLISION_SPRITE);
    }
    else {
        add_collision_mode(COLLISION_CUSTOM);
    }

    collision_tests.emplace_back(
        get_lua_context(),
        collision_test,
        callback_ref
    );
}
Пример #5
0
/**
 * \brief Registers a timer into a context (table or a userdata).
 * \param timer A timer.
 * \param context_index Index of the table or userdata in the stack.
 * \param callback_ref Lua ref to the function to call when the timer finishes.
 */
void LuaContext::add_timer(
    const TimerPtr& timer,
    int context_index,
    const ScopedLuaRef& callback_ref
) {
  const void* context;
  if (lua_type(l, context_index) == LUA_TUSERDATA) {
    ExportableToLuaPtr* userdata = static_cast<ExportableToLuaPtr*>(
        lua_touserdata(l, context_index)
    );
    context = userdata->get();
  }
  else {
    context = lua_topointer(l, context_index);
  }

  callback_ref.push();

#ifndef NDEBUG
  // Sanity check: check the uniqueness of the ref.
  for (const auto& kvp: timers) {
    if (kvp.second.callback_ref.get() == callback_ref.get()) {
      std::ostringstream oss;
      oss << "Callback ref " << callback_ref.get()
          << " is already used by a timer (duplicate luaL_unref?)";
      Debug::die(oss.str());
    }
  }
#endif

  Debug::check_assertion(timers.find(timer) == timers.end(),
      "Duplicate timer in the system");

  timers[timer].callback_ref = callback_ref;
  timers[timer].context = context;

  Game* game = main_loop.get_game();
  if (game != nullptr) {
    // We are during a game: depending on the timer's context,
    // suspend the timer or not.
    if (is_map(l, context_index)
        || is_entity(l, context_index)
        || is_item(l, context_index)) {

      bool initially_suspended = false;

      // By default, we want the timer to be automatically suspended when a
      // camera movement, a dialog or the pause menu starts.
      if (!is_entity(l, context_index)) {
        // The timer normally gets suspended/resumed with the map.
        timer->set_suspended_with_map(true);

        // But in the initial state, we override that rule.
        // We initially suspend the timer only during a dialog.
        // In particular, we don't want to suspend timers created during a
        // camera movement.
        // This would be very painful for users.
        initially_suspended = game->is_dialog_enabled();
      }
      else {
        // Entities are more complex: they also get suspended when disabled
        // and when far from the camera. Therefore, they don't simply follow
        // the map suspended state.
        EntityPtr entity = check_entity(l, context_index);
        initially_suspended = entity->is_suspended() || !entity->is_enabled();
      }

      timer->set_suspended(initially_suspended);
    }
  }
}
Пример #6
0
/**
 * \brief Opens the dialog box to show a dialog.
 *
 * No other dialog should be already running.
 *
 * \param dialog_id Id of the dialog to show.
 * \param info_ref Lua ref to an optional info parameter to pass to the
 * dialog box, or an empty ref.
 * \param callback_ref Lua ref to a function to call when the dialog finishes,
 * or an empty ref.
 */
void DialogBoxSystem::open(
    const std::string& dialog_id,
    const ScopedLuaRef& info_ref,
    const ScopedLuaRef& callback_ref
) {
  Debug::check_assertion(!is_enabled(), "A dialog is already active");

  this->dialog_id = dialog_id;
  this->dialog = DialogResource::get_dialog(dialog_id);
  this->callback_ref = callback_ref;

  // Save commands.
  KeysEffect& keys_effect = game.get_keys_effect();
  keys_effect.save_action_key_effect();
  keys_effect.set_action_key_effect(KeysEffect::ACTION_KEY_NONE);
  keys_effect.save_sword_key_effect();
  keys_effect.set_sword_key_effect(KeysEffect::SWORD_KEY_NONE);
  keys_effect.save_pause_key_effect();
  keys_effect.set_pause_key_effect(KeysEffect::PAUSE_KEY_NONE);

  // A dialog was just started: notify Lua.
  LuaContext& lua_context = game.get_lua_context();
  lua_State* l = lua_context.get_internal_state();
  built_in = !lua_context.notify_dialog_started(
      game, dialog, info_ref
  );

  if (built_in) {

    // Show a built-in default dialog box.
    keys_effect.set_action_key_effect(KeysEffect::ACTION_KEY_NEXT);

    // Prepare the text.
    std::string text = dialog.get_text();
    this->is_question = false;

    if (dialog_id == "_shop.question") {
      // Built-in dialog with the "do you want to buy" question and the price.
      this->is_question = true;
      size_t index = text.find("$v");
      if (index != std::string::npos) {
        // Replace the special sequence '$v' by the price of the shop item.
        info_ref.push();
        int price = LuaTools::check_int(l, -1);
        lua_pop(l, -1);
        std::ostringstream oss;
        oss << price;
        text = text.replace(index, 2, oss.str());
      }
    }

    remaining_lines.clear();
    std::istringstream iss(text);
    std::string line;
    while (std::getline(iss, line)) {
      remaining_lines.push_back(line);
    }

    // Determine the position.
    const Rectangle& camera_position = game.get_current_map().get_camera_position();
    bool top = false;
    if (game.get_hero()->get_y() >= camera_position.get_y() + 130) {
      top = true;
    }
    int x = camera_position.get_width() / 2 - 110;
    int y = top ? 32 : camera_position.get_height() - 96;

    text_position = { x, y };

    // Start showing text.
    show_more_lines();
  }
}