/** * \brief Unregisters all menus associated to a context. * * This function can be called safely even while iterating on the menus list. * * \param context_index Index of a table or userdata containing menus. */ void LuaContext::remove_menus(int context_index) { 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); } // Some menu:on_finished() callbacks may create menus themselves, // and we don't want those new menus to get removed. for (LuaMenuData& menu: menus) { menu.recently_added = false; } for (LuaMenuData& menu: menus) { ScopedLuaRef menu_ref = menu.ref; if (menu.context == context && !menu.recently_added) { menu.ref.clear(); menu.context = nullptr; menu_on_finished(menu_ref); } } }
/** * \brief Calls the on_draw() method of the menus associated to a context. * \param context_index Index of an object with menus. * \param dst_surface The destination surface to draw. */ void LuaContext::menus_on_draw(int context_index, const SurfacePtr& dst_surface) { 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); } for (LuaMenuData& menu: menus) { if (menu.context == context) { menu_on_draw(menu.ref, dst_surface); } } }
/** * \brief Unregisters all timers associated to a context. * * This function can be called safely even while iterating on the timer list. * * \param context_index Index of a table or userdata containing timers. */ void LuaContext::remove_timers(int context_index) { 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); } for (auto& kvp: timers) { const TimerPtr& timer = kvp.first; if (kvp.second.context == context) { kvp.second.callback_ref.clear(); timers_to_remove.push_back(timer); } } }
/** * \brief Calls the on_input() method of the menus associated to a context. * \param context_index Index of an object with menus. * \param event The input event to handle. * \return \c true if the event was handled and should stop being propagated. */ bool LuaContext::menus_on_input(int context_index, const InputEvent& event) { 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); } bool handled = false; std::list<LuaMenuData>::reverse_iterator it; for (it = menus.rbegin(); it != menus.rend() && !handled; ++it) { const ScopedLuaRef& menu_ref = it->ref; if (it->context == context) { handled = menu_on_input(menu_ref, event); } } return handled; }
/** * \brief Registers a menu into a context (table or a userdata). * * This function can be called safely even while iterating on the menus list. * * \param menu_ref Lua ref of the menu to add. * \param context_index Index of the table or userdata in the stack. * \param on_top \c true to place this menu on top of existing one in the * same context, \c false to place it behind. */ void LuaContext::add_menu( const ScopedLuaRef& menu_ref, int context_index, bool on_top ) { 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); } if (on_top) { menus.emplace_back(menu_ref, context); } else { menus.emplace_front(menu_ref, context); } menu_on_started(menu_ref); }
/** * \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); } } }