Exemplo n.º 1
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);
    }
  }
}