mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-30 03:32:36 +00:00
Control GUI modes from Lua
This commit is contained in:
parent
f86a45d60a
commit
87eacf774a
@ -7,6 +7,7 @@
|
||||
|
||||
#include <SDL_events.h>
|
||||
|
||||
#include "../mwgui/mode.hpp"
|
||||
#include <components/sdlutil/events.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
@ -52,6 +53,10 @@ namespace MWBase
|
||||
virtual void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;
|
||||
virtual void exteriorCreated(MWWorld::CellStore& cell) = 0;
|
||||
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
||||
|
||||
// `arg` is either forwarded from MWGui::pushGuiMode or empty
|
||||
virtual void uiModeChanged(const MWWorld::Ptr& arg) = 0;
|
||||
|
||||
// TODO: notify LuaManager about other events
|
||||
// virtual void objectOnHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object,
|
||||
// const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) = 0;
|
||||
|
@ -378,6 +378,12 @@ namespace MWBase
|
||||
|
||||
/// Same as viewer->getCamera()->getCullMask(), provided for consistency.
|
||||
virtual uint32_t getCullMask() = 0;
|
||||
|
||||
// Used in Lua bindings
|
||||
virtual const std::vector<MWGui::GuiMode>& getGuiModeStack() const = 0;
|
||||
virtual void setDisabledByLua(std::string_view windowId, bool disabled) = 0;
|
||||
virtual std::vector<std::string_view> getAllWindowIds() const = 0;
|
||||
virtual std::vector<std::string_view> getAllowedWindowIds(MWGui::GuiMode mode) const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,8 @@ namespace MWGui
|
||||
|
||||
void onResChange(int, int) override { center(); }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Alchemy"; }
|
||||
|
||||
private:
|
||||
static const float sCountChangeInitialPause; // in seconds
|
||||
static const float sCountChangeInterval; // in seconds
|
||||
|
@ -19,6 +19,8 @@ namespace MWGui
|
||||
|
||||
void onResChange(int, int) override { center(); }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Book"; }
|
||||
|
||||
protected:
|
||||
void onNextPageButtonClicked(MyGUI::Widget* sender);
|
||||
void onPrevPageButtonClicked(MyGUI::Widget* sender);
|
||||
|
@ -30,6 +30,8 @@ namespace MWGui
|
||||
void onFrame(float dt) override;
|
||||
void clear() override { resetReference(); }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Companion"; }
|
||||
|
||||
private:
|
||||
ItemView* mItemView;
|
||||
SortFilterItemModel* mSortModel;
|
||||
|
@ -38,6 +38,8 @@ namespace MWGui
|
||||
|
||||
void treatNextOpenAsLoot() { mTreatNextOpenAsLoot = true; }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Container"; }
|
||||
|
||||
private:
|
||||
DragAndDrop* mDragAndDrop;
|
||||
|
||||
|
@ -165,6 +165,8 @@ namespace MWGui
|
||||
|
||||
void onClose() override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Dialogue"; }
|
||||
|
||||
protected:
|
||||
void updateTopicsPane();
|
||||
bool isCompanion(const MWWorld::Ptr& actor);
|
||||
|
@ -33,6 +33,8 @@ namespace MWGui
|
||||
|
||||
void resetReference() override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "EnchantingDialog"; }
|
||||
|
||||
protected:
|
||||
void onReferenceUnavailable() override;
|
||||
void notifyEffectsChanged() override;
|
||||
|
@ -65,6 +65,8 @@ namespace MWGui
|
||||
/// Cycle to previous/next weapon
|
||||
void cycle(bool next);
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Inventory"; }
|
||||
|
||||
protected:
|
||||
void onTitleDoubleClicked() override;
|
||||
|
||||
|
@ -16,6 +16,8 @@ namespace MWGui
|
||||
|
||||
bool exit() override { return false; }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "JailScreen"; }
|
||||
|
||||
private:
|
||||
int mDays;
|
||||
|
||||
|
@ -29,6 +29,8 @@ namespace MWGui
|
||||
|
||||
/// show/hide the journal window
|
||||
void setVisible(bool newValue) override = 0;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Journal"; }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,8 @@ namespace MWGui
|
||||
|
||||
void onOpen() override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "LevelUpDialog"; }
|
||||
|
||||
private:
|
||||
struct Widgets
|
||||
{
|
||||
|
@ -268,6 +268,8 @@ namespace MWGui
|
||||
|
||||
void asyncPrepareSaveMap();
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Map"; }
|
||||
|
||||
private:
|
||||
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
|
@ -16,6 +16,8 @@ namespace MWGui
|
||||
|
||||
void setPtr(const MWWorld::Ptr& actor) override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "MerchantRepair"; }
|
||||
|
||||
private:
|
||||
MyGUI::ScrollView* mList;
|
||||
MyGUI::Button* mOkButton;
|
||||
|
@ -44,6 +44,8 @@ namespace MWGui
|
||||
void readRecord(ESM::ESMReader& reader, uint32_t type);
|
||||
void clear() override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "QuickKeys"; }
|
||||
|
||||
private:
|
||||
struct keyData
|
||||
{
|
||||
|
@ -26,6 +26,8 @@ namespace MWGui
|
||||
|
||||
void setPtr(const MWWorld::Ptr& gem) override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Recharge"; }
|
||||
|
||||
protected:
|
||||
ItemChargeView* mBox;
|
||||
|
||||
|
@ -23,6 +23,8 @@ namespace MWGui
|
||||
|
||||
void setPtr(const MWWorld::Ptr& item) override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Repair"; }
|
||||
|
||||
protected:
|
||||
ItemChargeView* mRepairBox;
|
||||
|
||||
|
@ -22,6 +22,8 @@ namespace MWGui
|
||||
|
||||
void onResChange(int, int) override { center(); }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Scroll"; }
|
||||
|
||||
protected:
|
||||
void onCloseButtonClicked(MyGUI::Widget* _sender);
|
||||
void onTakeButtonClicked(MyGUI::Widget* _sender);
|
||||
|
@ -30,6 +30,8 @@ namespace MWGui
|
||||
|
||||
void onResChange(int, int) override { center(); }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "SpellBuying"; }
|
||||
|
||||
protected:
|
||||
MyGUI::Button* mCancelButton;
|
||||
MyGUI::TextBox* mPlayerGold;
|
||||
|
@ -158,6 +158,8 @@ namespace MWGui
|
||||
|
||||
void setPtr(const MWWorld::Ptr& actor) override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "SpellCreationDialog"; }
|
||||
|
||||
protected:
|
||||
void onReferenceUnavailable() override;
|
||||
|
||||
|
@ -23,6 +23,8 @@ namespace MWGui
|
||||
/// Cycle to next/previous spell
|
||||
void cycle(bool next);
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Magic"; }
|
||||
|
||||
protected:
|
||||
MyGUI::Widget* mEffectBox;
|
||||
|
||||
|
@ -45,6 +45,8 @@ namespace MWGui
|
||||
|
||||
void onOpen() override { onWindowResize(mMainWidget->castType<MyGUI::Window>()); }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Stats"; }
|
||||
|
||||
private:
|
||||
void addSkills(const std::vector<ESM::RefId>& skills, const std::string& titleId,
|
||||
const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);
|
||||
|
@ -47,6 +47,8 @@ namespace MWGui
|
||||
typedef MyGUI::delegates::MultiDelegate<> EventHandle_TradeDone;
|
||||
EventHandle_TradeDone eventTradeDone;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Trade"; }
|
||||
|
||||
private:
|
||||
ItemView* mItemView;
|
||||
SortFilterItemModel* mSortModel;
|
||||
|
@ -31,6 +31,8 @@ namespace MWGui
|
||||
|
||||
void clear() override { resetReference(); }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Training"; }
|
||||
|
||||
protected:
|
||||
void onReferenceUnavailable() override;
|
||||
|
||||
|
@ -19,6 +19,8 @@ namespace MWGui
|
||||
|
||||
void setPtr(const MWWorld::Ptr& actor) override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Travel"; }
|
||||
|
||||
protected:
|
||||
MyGUI::Button* mCancelButton;
|
||||
MyGUI::TextBox* mPlayerGold;
|
||||
|
@ -43,6 +43,8 @@ namespace MWGui
|
||||
|
||||
WindowBase* getProgressBar() { return &mProgressBar; }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "WaitDialog"; }
|
||||
|
||||
protected:
|
||||
MyGUI::TextBox* mDateTimeText;
|
||||
MyGUI::TextBox* mRestText;
|
||||
|
@ -48,6 +48,7 @@ void WindowBase::onDoubleClick(MyGUI::Widget* _sender)
|
||||
|
||||
void WindowBase::setVisible(bool visible)
|
||||
{
|
||||
visible = visible && !mDisabledByLua;
|
||||
bool wasVisible = mMainWidget->getVisible();
|
||||
mMainWidget->setVisible(visible);
|
||||
|
||||
|
@ -49,11 +49,16 @@ namespace MWGui
|
||||
|
||||
virtual void onDeleteCustomData(const MWWorld::Ptr& ptr) {}
|
||||
|
||||
virtual std::string_view getWindowIdForLua() const { return ""; }
|
||||
void setDisabledByLua(bool disabled) { mDisabledByLua = disabled; }
|
||||
|
||||
protected:
|
||||
virtual void onTitleDoubleClicked();
|
||||
|
||||
private:
|
||||
void onDoubleClick(MyGUI::Widget* _sender);
|
||||
|
||||
bool mDisabledByLua = false;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -525,6 +525,13 @@ namespace MWGui
|
||||
mStatsWatcher->addListener(mHud);
|
||||
mStatsWatcher->addListener(mStatsWindow);
|
||||
mStatsWatcher->addListener(mCharGen.get());
|
||||
|
||||
for (auto& window : mWindows)
|
||||
{
|
||||
std::string_view id = window->getWindowIdForLua();
|
||||
if (!id.empty())
|
||||
mLuaIdToWindow.emplace(id, window.get());
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::setNewGame(bool newgame)
|
||||
@ -1277,6 +1284,7 @@ namespace MWGui
|
||||
mKeyboardNavigation->restoreFocus(mode);
|
||||
|
||||
updateVisible();
|
||||
MWBase::Environment::get().getLuaManager()->uiModeChanged(arg);
|
||||
}
|
||||
|
||||
void WindowManager::setCullMask(uint32_t mask)
|
||||
@ -1309,6 +1317,7 @@ namespace MWGui
|
||||
mGuiModeStates[mode].update(false);
|
||||
if (!noSound)
|
||||
playSound(mGuiModeStates[mode].mCloseSound);
|
||||
MWBase::Environment::get().getLuaManager()->uiModeChanged(MWWorld::Ptr());
|
||||
}
|
||||
|
||||
if (!mGuiModes.empty())
|
||||
@ -1343,6 +1352,7 @@ namespace MWGui
|
||||
}
|
||||
|
||||
updateVisible();
|
||||
MWBase::Environment::get().getLuaManager()->uiModeChanged(MWWorld::Ptr());
|
||||
}
|
||||
|
||||
void WindowManager::goToJail(int days)
|
||||
@ -1748,7 +1758,10 @@ namespace MWGui
|
||||
mPlayerBounty = -1;
|
||||
|
||||
for (const auto& window : mWindows)
|
||||
{
|
||||
window->clear();
|
||||
window->setDisabledByLua(false);
|
||||
}
|
||||
|
||||
if (mLocalMapRender)
|
||||
mLocalMapRender->clear();
|
||||
@ -2334,4 +2347,45 @@ namespace MWGui
|
||||
{
|
||||
mMap->asyncPrepareSaveMap();
|
||||
}
|
||||
|
||||
void WindowManager::setDisabledByLua(std::string_view windowId, bool disabled)
|
||||
{
|
||||
mLuaIdToWindow.at(windowId)->setDisabledByLua(disabled);
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
std::vector<std::string_view> WindowManager::getAllWindowIds() const
|
||||
{
|
||||
std::vector<std::string_view> res;
|
||||
for (const auto& [id, _] : mLuaIdToWindow)
|
||||
res.push_back(id);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> WindowManager::getAllowedWindowIds(GuiMode mode) const
|
||||
{
|
||||
std::vector<std::string_view> res;
|
||||
if (mode == GM_Inventory)
|
||||
{
|
||||
if (mAllowed & GW_Map)
|
||||
res.push_back(mMap->getWindowIdForLua());
|
||||
if (mAllowed & GW_Inventory)
|
||||
res.push_back(mInventoryWindow->getWindowIdForLua());
|
||||
if (mAllowed & GW_Magic)
|
||||
res.push_back(mSpellWindow->getWindowIdForLua());
|
||||
if (mAllowed & GW_Stats)
|
||||
res.push_back(mStatsWindow->getWindowIdForLua());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = mGuiModeStates.find(mode);
|
||||
if (it != mGuiModeStates.end())
|
||||
{
|
||||
for (const auto* w : it->second.mWindows)
|
||||
if (!w->getWindowIdForLua().empty())
|
||||
res.push_back(w->getWindowIdForLua());
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@ -387,6 +387,12 @@ namespace MWGui
|
||||
|
||||
void asyncPrepareSaveMap() override;
|
||||
|
||||
// Used in Lua bindings
|
||||
const std::vector<GuiMode>& getGuiModeStack() const override { return mGuiModes; }
|
||||
void setDisabledByLua(std::string_view windowId, bool disabled) override;
|
||||
std::vector<std::string_view> getAllWindowIds() const override;
|
||||
std::vector<std::string_view> getAllowedWindowIds(GuiMode mode) const override;
|
||||
|
||||
private:
|
||||
unsigned int mOldUpdateMask;
|
||||
unsigned int mOldCullMask;
|
||||
@ -451,6 +457,9 @@ namespace MWGui
|
||||
|
||||
std::vector<std::unique_ptr<WindowBase>> mWindows;
|
||||
|
||||
// Mapping windowId -> Window; used by Lua bindings.
|
||||
std::map<std::string_view, WindowBase*> mLuaIdToWindow;
|
||||
|
||||
Translation::Storage& mTranslationDataStorage;
|
||||
|
||||
std::unique_ptr<CharacterCreation> mCharGen;
|
||||
|
@ -121,7 +121,7 @@ namespace MWLua
|
||||
{
|
||||
auto* lua = context.mLua;
|
||||
sol::table api(lua->sol(), sol::create);
|
||||
api["API_REVISION"] = 43;
|
||||
api["API_REVISION"] = 44;
|
||||
api["quit"] = [lua]() {
|
||||
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||
MWBase::Environment::get().getStateManager()->requestQuit();
|
||||
|
@ -311,6 +311,15 @@ namespace MWLua
|
||||
mGlobalScriptsStarted = true;
|
||||
}
|
||||
|
||||
void LuaManager::uiModeChanged(const MWWorld::Ptr& arg)
|
||||
{
|
||||
if (mPlayer.isEmpty())
|
||||
return;
|
||||
PlayerScripts* playerScripts = dynamic_cast<PlayerScripts*>(mPlayer.getRefData().getLuaScripts());
|
||||
if (playerScripts)
|
||||
playerScripts->uiModeChanged(arg);
|
||||
}
|
||||
|
||||
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
|
||||
|
@ -83,6 +83,7 @@ namespace MWLua
|
||||
}
|
||||
void objectTeleported(const MWWorld::Ptr& ptr) override;
|
||||
void questUpdated(const ESM::RefId& questId, int stage) override;
|
||||
void uiModeChanged(const MWWorld::Ptr& arg) override;
|
||||
|
||||
MWBase::LuaManager::ActorControls* getActorControls(const MWWorld::Ptr&) const override;
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace MWLua
|
||||
{
|
||||
registerEngineHandlers({ &mConsoleCommandHandlers, &mKeyPressHandlers, &mKeyReleaseHandlers,
|
||||
&mControllerButtonPressHandlers, &mControllerButtonReleaseHandlers, &mActionHandlers, &mOnFrameHandlers,
|
||||
&mTouchpadPressed, &mTouchpadReleased, &mTouchpadMoved, &mQuestUpdate });
|
||||
&mTouchpadPressed, &mTouchpadReleased, &mTouchpadMoved, &mQuestUpdate, &mUiModeChanged });
|
||||
}
|
||||
|
||||
void processInputEvent(const MWBase::LuaManager::InputEvent& event)
|
||||
@ -65,6 +65,15 @@ namespace MWLua
|
||||
return !mConsoleCommandHandlers.mList.empty();
|
||||
}
|
||||
|
||||
// `arg` is either forwarded from MWGui::pushGuiMode or empty
|
||||
void uiModeChanged(const MWWorld::Ptr& arg)
|
||||
{
|
||||
if (arg.isEmpty())
|
||||
callEngineHandlers(mUiModeChanged);
|
||||
else
|
||||
callEngineHandlers(mUiModeChanged, LObject(arg));
|
||||
}
|
||||
|
||||
private:
|
||||
EngineHandlerList mConsoleCommandHandlers{ "onConsoleCommand" };
|
||||
EngineHandlerList mKeyPressHandlers{ "onKeyPress" };
|
||||
@ -77,6 +86,7 @@ namespace MWLua
|
||||
EngineHandlerList mTouchpadReleased{ "onTouchRelease" };
|
||||
EngineHandlerList mTouchpadMoved{ "onTouchMove" };
|
||||
EngineHandlerList mQuestUpdate{ "onQuestUpdate" };
|
||||
EngineHandlerList mUiModeChanged{ "_onUiModeChanged" };
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -45,10 +45,55 @@ namespace MWLua
|
||||
{
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
const std::unordered_map<MWGui::GuiMode, std::string_view> modeToName{
|
||||
{ MWGui::GM_Settings, "SettingsMenu" },
|
||||
{ MWGui::GM_Inventory, "Interface" },
|
||||
{ MWGui::GM_Container, "Container" },
|
||||
{ MWGui::GM_Companion, "Companion" },
|
||||
{ MWGui::GM_MainMenu, "MainMenu" },
|
||||
{ MWGui::GM_Journal, "Journal" },
|
||||
{ MWGui::GM_Scroll, "Scroll" },
|
||||
{ MWGui::GM_Book, "Book" },
|
||||
{ MWGui::GM_Alchemy, "Alchemy" },
|
||||
{ MWGui::GM_Repair, "Repair" },
|
||||
{ MWGui::GM_Dialogue, "Dialogue" },
|
||||
{ MWGui::GM_Barter, "Barter" },
|
||||
{ MWGui::GM_Rest, "Rest" },
|
||||
{ MWGui::GM_SpellBuying, "SpellBuying" },
|
||||
{ MWGui::GM_Travel, "Travel" },
|
||||
{ MWGui::GM_SpellCreation, "SpellCreation" },
|
||||
{ MWGui::GM_Enchanting, "Enchanting" },
|
||||
{ MWGui::GM_Recharge, "Recharge" },
|
||||
{ MWGui::GM_Training, "Training" },
|
||||
{ MWGui::GM_MerchantRepair, "MerchantRepair" },
|
||||
{ MWGui::GM_Levelup, "LevelUp" },
|
||||
{ MWGui::GM_Name, "ChargenName" },
|
||||
{ MWGui::GM_Race, "ChargenRace" },
|
||||
{ MWGui::GM_Birth, "ChargenBirth" },
|
||||
{ MWGui::GM_Class, "ChargenClass" },
|
||||
{ MWGui::GM_ClassGenerate, "ChargenClassGenerate" },
|
||||
{ MWGui::GM_ClassPick, "ChargenClassPick" },
|
||||
{ MWGui::GM_ClassCreate, "ChargenClassCreate" },
|
||||
{ MWGui::GM_Review, "ChargenClassReview" },
|
||||
{ MWGui::GM_Loading, "Loading" },
|
||||
{ MWGui::GM_LoadingWallpaper, "LoadingWallpaper" },
|
||||
{ MWGui::GM_Jail, "Jail" },
|
||||
{ MWGui::GM_QuickKeysMenu, "QuickKeysMenu" },
|
||||
};
|
||||
|
||||
const auto nameToMode = [] {
|
||||
std::unordered_map<std::string_view, MWGui::GuiMode> res;
|
||||
for (const auto& [mode, name] : modeToName)
|
||||
res[name] = mode;
|
||||
return res;
|
||||
}();
|
||||
}
|
||||
|
||||
sol::table initUserInterfacePackage(const Context& context)
|
||||
{
|
||||
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();
|
||||
|
||||
auto element = context.mLua->sol().new_usertype<LuaUi::Element>("Element");
|
||||
element["layout"] = sol::property([](LuaUi::Element& element) { return element.mLayout; },
|
||||
[](LuaUi::Element& element, const sol::table& layout) { element.mLayout = layout; });
|
||||
@ -78,19 +123,18 @@ namespace MWLua
|
||||
= [luaManager = context.mLuaManager](const std::string& message, const Misc::Color& color) {
|
||||
luaManager->addInGameConsoleMessage(message + "\n", color);
|
||||
};
|
||||
api["setConsoleMode"] = [luaManager = context.mLuaManager](std::string_view mode) {
|
||||
luaManager->addAction(
|
||||
[mode = std::string(mode)] { MWBase::Environment::get().getWindowManager()->setConsoleMode(mode); });
|
||||
api["setConsoleMode"] = [luaManager = context.mLuaManager, windowManager](std::string_view mode) {
|
||||
luaManager->addAction([mode = std::string(mode), windowManager] { windowManager->setConsoleMode(mode); });
|
||||
};
|
||||
api["setConsoleSelectedObject"] = [luaManager = context.mLuaManager](const sol::object& obj) {
|
||||
const auto wm = MWBase::Environment::get().getWindowManager();
|
||||
api["setConsoleSelectedObject"] = [luaManager = context.mLuaManager, windowManager](const sol::object& obj) {
|
||||
if (obj == sol::nil)
|
||||
luaManager->addAction([wm] { wm->setConsoleSelectedObject(MWWorld::Ptr()); });
|
||||
luaManager->addAction([windowManager] { windowManager->setConsoleSelectedObject(MWWorld::Ptr()); });
|
||||
else
|
||||
{
|
||||
if (!obj.is<LObject>())
|
||||
throw std::runtime_error("Game object expected");
|
||||
luaManager->addAction([wm, obj = obj.as<LObject>()] { wm->setConsoleSelectedObject(obj.ptr()); });
|
||||
luaManager->addAction(
|
||||
[windowManager, obj = obj.as<LObject>()] { windowManager->setConsoleSelectedObject(obj.ptr()); });
|
||||
}
|
||||
};
|
||||
api["content"] = LuaUi::loadContentConstructor(context.mLua);
|
||||
@ -189,6 +233,65 @@ namespace MWLua
|
||||
Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video"));
|
||||
};
|
||||
|
||||
api["_getAllUiModes"] = [](sol::this_state lua) {
|
||||
sol::table res(lua, sol::create);
|
||||
for (const auto& [_, name] : modeToName)
|
||||
res[name] = name;
|
||||
return res;
|
||||
};
|
||||
api["_getUiModeStack"] = [windowManager](sol::this_state lua) {
|
||||
sol::table res(lua, sol::create);
|
||||
int i = 1;
|
||||
for (MWGui::GuiMode m : windowManager->getGuiModeStack())
|
||||
res[i++] = modeToName.at(m);
|
||||
return res;
|
||||
};
|
||||
api["_setUiModeStack"]
|
||||
= [windowManager, luaManager = context.mLuaManager](sol::table modes, sol::optional<LObject> arg) {
|
||||
std::vector<MWGui::GuiMode> newStack(modes.size());
|
||||
for (unsigned i = 0; i < newStack.size(); ++i)
|
||||
newStack[i] = nameToMode.at(LuaUtil::cast<std::string_view>(modes[i + 1]));
|
||||
luaManager->addAction(
|
||||
[windowManager, newStack, arg]() {
|
||||
MWWorld::Ptr ptr;
|
||||
if (arg.has_value())
|
||||
ptr = arg->ptr();
|
||||
const std::vector<MWGui::GuiMode>& stack = windowManager->getGuiModeStack();
|
||||
unsigned common = 0;
|
||||
while (common < std::min(stack.size(), newStack.size()) && stack[common] == newStack[common])
|
||||
common++;
|
||||
// TODO: Maybe disallow opening/closing special modes (main menu, settings, loading screen)
|
||||
// from player scripts. Add new Lua context "menu" that can do it.
|
||||
for (unsigned i = stack.size() - common; i > 0; i--)
|
||||
windowManager->popGuiMode();
|
||||
if (common == newStack.size() && !newStack.empty() && arg.has_value())
|
||||
windowManager->pushGuiMode(newStack.back(), ptr);
|
||||
for (unsigned i = common; i < newStack.size(); ++i)
|
||||
windowManager->pushGuiMode(newStack[i], ptr);
|
||||
},
|
||||
"Set UI modes");
|
||||
};
|
||||
api["_getAllWindowIds"] = [windowManager](sol::this_state lua) {
|
||||
sol::table res(lua, sol::create);
|
||||
for (std::string_view name : windowManager->getAllWindowIds())
|
||||
res[name] = name;
|
||||
return res;
|
||||
};
|
||||
api["_getAllowedWindows"] = [windowManager](sol::this_state lua, std::string_view mode) {
|
||||
sol::table res(lua, sol::create);
|
||||
for (std::string_view name : windowManager->getAllowedWindowIds(nameToMode.at(mode)))
|
||||
res[name] = name;
|
||||
return res;
|
||||
};
|
||||
api["_setWindowDisabled"]
|
||||
= [windowManager, luaManager = context.mLuaManager](std::string_view window, bool disabled) {
|
||||
luaManager->addAction([=]() { windowManager->setDisabledByLua(window, disabled); });
|
||||
};
|
||||
|
||||
// TODO
|
||||
// api["_showHUD"] = [](bool) {};
|
||||
// api["_showMouseCursor"] = [](bool) {};
|
||||
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
}
|
||||
|
@ -204,8 +204,7 @@ CUSTOM, PLAYER: useInterface.lua
|
||||
{
|
||||
testing::internal::CaptureStdout();
|
||||
scripts.receiveEvent("SomeEvent", X1);
|
||||
EXPECT_EQ(internal::GetCapturedStdout(),
|
||||
"Test has received event 'SomeEvent', but there are no handlers for this event\n");
|
||||
EXPECT_EQ(internal::GetCapturedStdout(), "");
|
||||
}
|
||||
{
|
||||
testing::internal::CaptureStdout();
|
||||
|
@ -297,11 +297,7 @@ namespace LuaUtil
|
||||
{
|
||||
auto it = mEventHandlers.find(eventName);
|
||||
if (it == mEventHandlers.end())
|
||||
{
|
||||
Log(Debug::Warning) << mNamePrefix << " has received event '" << eventName
|
||||
<< "', but there are no handlers for this event";
|
||||
return;
|
||||
}
|
||||
sol::object data;
|
||||
try
|
||||
{
|
||||
|
@ -6,5 +6,6 @@ paths=(
|
||||
scripts/omw/camera/camera.lua
|
||||
scripts/omw/mwui/init.lua
|
||||
scripts/omw/settings/player.lua
|
||||
scripts/omw/ui.lua
|
||||
)
|
||||
printf '%s\n' "${paths[@]}"
|
@ -34,6 +34,7 @@ Lua API reference
|
||||
interface_controls
|
||||
interface_mwui
|
||||
interface_settings
|
||||
interface_ui
|
||||
iterables
|
||||
|
||||
|
||||
@ -90,3 +91,7 @@ Interfaces of built-in scripts
|
||||
* - :ref:`MWUI <Interface MWUI>`
|
||||
- by player scripts
|
||||
- Morrowind-style UI templates.
|
||||
* - :ref:`UI <Interface UI>`
|
||||
- by player scripts
|
||||
- | High-level UI modes interface. Allows to override parts
|
||||
| of the interface.
|
||||
|
@ -1,6 +1,9 @@
|
||||
Built-in events
|
||||
===============
|
||||
|
||||
Actor events
|
||||
------------
|
||||
|
||||
Any script can send to any actor (except player, for player will be ignored) events ``StartAIPackage`` and ``RemoveAIPackages``.
|
||||
The effect is equivalent to calling ``interfaces.AI.startPackage`` or ``interfaces.AI.removePackages`` in a local script on this actor.
|
||||
|
||||
@ -11,3 +14,16 @@ Examples:
|
||||
actor:sendEvent('StartAIPackage', {type='Combat', target=self.object})
|
||||
actor:sendEvent('RemoveAIPackages', 'Pursue')
|
||||
|
||||
UI events
|
||||
---------
|
||||
|
||||
Every time UI mode is changed built-in scripts send to player the event ``UiModeChanged`` with arguments ``mode`` (same as ``I.UI.getMode()``)
|
||||
and ``arg`` (for example in the mode ``Book`` the argument is the book the player is reading).
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
eventHandlers = {
|
||||
UiModeChanged = function(data)
|
||||
print('UiModeChanged to', data.mode, '('..tostring(data.arg)..')')
|
||||
end
|
||||
}
|
||||
|
6
docs/source/reference/lua-scripting/interface_ui.rst
Normal file
6
docs/source/reference/lua-scripting/interface_ui.rst
Normal file
@ -0,0 +1,6 @@
|
||||
Interface UI
|
||||
============
|
||||
|
||||
.. raw:: html
|
||||
:file: generated_html/scripts_omw_ui.html
|
||||
|
@ -481,6 +481,10 @@ The order in which the scripts are started is important. So if one mod should ov
|
||||
* - :ref:`MWUI <Interface MWUI>`
|
||||
- by player scripts
|
||||
- Morrowind-style UI templates.
|
||||
* - :ref:`UI <Interface UI>`
|
||||
- by player scripts
|
||||
- | High-level UI modes interface. Allows to override parts
|
||||
| of the interface.
|
||||
|
||||
Event system
|
||||
============
|
||||
|
@ -89,6 +89,7 @@ set(BUILTIN_DATA_FILES
|
||||
scripts/omw/mwui/textEdit.lua
|
||||
scripts/omw/mwui/space.lua
|
||||
scripts/omw/mwui/init.lua
|
||||
scripts/omw/ui.lua
|
||||
|
||||
shaders/adjustments.omwfx
|
||||
shaders/bloomlinear.omwfx
|
||||
|
@ -13,6 +13,9 @@ PLAYER: scripts/omw/playercontrols.lua
|
||||
PLAYER: scripts/omw/camera/camera.lua
|
||||
NPC,CREATURE: scripts/omw/ai.lua
|
||||
|
||||
# User interface
|
||||
PLAYER: scripts/omw/ui.lua
|
||||
|
||||
# Lua console
|
||||
PLAYER: scripts/omw/console/player.lua
|
||||
GLOBAL: scripts/omw/console/global.lua
|
||||
|
187
files/data/scripts/omw/ui.lua
Normal file
187
files/data/scripts/omw/ui.lua
Normal file
@ -0,0 +1,187 @@
|
||||
local ui = require('openmw.ui')
|
||||
local util = require('openmw.util')
|
||||
local self = require('openmw.self')
|
||||
|
||||
local MODE = ui._getAllUiModes()
|
||||
local WINDOW = ui._getAllWindowIds()
|
||||
|
||||
local replacedWindows = {}
|
||||
local hiddenWindows = {}
|
||||
local modeStack = {}
|
||||
|
||||
local function registerWindow(window, showFn, hideFn)
|
||||
if not WINDOW[window] then
|
||||
error('At the moment it is only possible to override existing windows. Window "'..
|
||||
tostring(window)..'" not found.')
|
||||
end
|
||||
ui._setWindowDisabled(window, true)
|
||||
if replacedWindows[window] then
|
||||
replacedWindows[window].hideFn()
|
||||
end
|
||||
replacedWindows[window] = {showFn = showFn, hideFn = hideFn, visible = false}
|
||||
hiddenWindows[window] = nil
|
||||
end
|
||||
|
||||
local function updateHidden(mode, options)
|
||||
local toHide = {}
|
||||
if options and options.windows then
|
||||
for _, w in pairs(ui._getAllowedWindows(mode)) do
|
||||
toHide[w] = true
|
||||
end
|
||||
for _, w in pairs(options.windows) do
|
||||
toHide[w] = nil
|
||||
end
|
||||
end
|
||||
for w, _ in pairs(hiddenWindows) do
|
||||
if toHide[w] then
|
||||
toHide[w] = nil
|
||||
else
|
||||
hiddenWindows[w] = nil
|
||||
if not replacedWindows[w] then
|
||||
ui._setWindowDisabled(w, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
for w, _ in pairs(toHide) do
|
||||
hiddenWindows[w] = true
|
||||
if not replacedWindows[w] then
|
||||
ui._setWindowDisabled(w, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function setMode(mode, options)
|
||||
if mode then
|
||||
updateHidden(mode, options)
|
||||
ui._setUiModeStack({mode}, options and options.target)
|
||||
else
|
||||
ui._setUiModeStack({})
|
||||
end
|
||||
end
|
||||
|
||||
local function addMode(mode, options)
|
||||
updateHidden(mode, options)
|
||||
modeStack[#modeStack + 1] = mode
|
||||
ui._setUiModeStack(modeStack, options and options.target)
|
||||
end
|
||||
|
||||
local function removeMode(mode)
|
||||
local sizeBefore = #modeStack
|
||||
local j = 1
|
||||
for i = 1, sizeBefore do
|
||||
if modeStack[i] ~= mode then
|
||||
modeStack[j] = modeStack[i]
|
||||
j = j + 1
|
||||
end
|
||||
end
|
||||
for i = j, sizeBefore do modeStack[i] = nil end
|
||||
if sizeBefore > #modeStack then
|
||||
ui._setUiModeStack(modeStack)
|
||||
end
|
||||
end
|
||||
|
||||
local function onUiModeChanged(arg)
|
||||
local newStack = ui._getUiModeStack()
|
||||
for i = 1, math.max(#modeStack, #newStack) do
|
||||
modeStack[i] = newStack[i]
|
||||
end
|
||||
for w, state in pairs(replacedWindows) do
|
||||
if state.visible then
|
||||
state.hideFn()
|
||||
state.visible = false
|
||||
end
|
||||
end
|
||||
local mode = newStack[#newStack]
|
||||
if mode then
|
||||
for _, w in pairs(ui._getAllowedWindows(mode)) do
|
||||
local state = replacedWindows[w]
|
||||
if state and not hiddenWindows[w] then
|
||||
state.showFn(arg)
|
||||
state.visible = true
|
||||
end
|
||||
end
|
||||
end
|
||||
self:sendEvent('UiModeChanged', {mode = mode, arg = arg})
|
||||
end
|
||||
|
||||
return {
|
||||
interfaceName = 'UI',
|
||||
---
|
||||
-- @module UI
|
||||
-- @usage require('openmw.interfaces').UI
|
||||
interface = {
|
||||
--- Interface version
|
||||
-- @field [parent=#UI] #number version
|
||||
version = 0,
|
||||
|
||||
--- All available UI modes.
|
||||
-- Use `view(I.UI.MODE)` in `luap` console mode to see the list.
|
||||
-- @field [parent=#UI] #table MODE
|
||||
MODE = util.makeStrictReadOnly(MODE),
|
||||
|
||||
--- All windows.
|
||||
-- Use `view(I.UI.WINDOW)` in `luap` console mode to see the list.
|
||||
-- @field [parent=#UI] #table WINDOW
|
||||
WINDOW = util.makeStrictReadOnly(WINDOW),
|
||||
|
||||
--- Register new implementation for the window with given name; overrides previous implementation.
|
||||
-- Adding new windows is not supported yet. At the moment it is only possible to override built-in windows.
|
||||
-- @function [parent=#UI] registerWindow
|
||||
-- @param #string windowName
|
||||
-- @param #function showFn Callback that will be called when the window should become visible
|
||||
-- @param #function hideFn Callback that will be called when the window should be hidden
|
||||
registerWindow = registerWindow,
|
||||
|
||||
--- Returns windows that can be shown in given mode.
|
||||
-- @function [parent=#UI] getWindowsForMode
|
||||
-- @param #string mode
|
||||
-- @return #table
|
||||
getWindowsForMode = ui._getAllowedWindows,
|
||||
|
||||
--- Stack of currently active modes
|
||||
-- @field [parent=#UI] modes
|
||||
modes = util.makeReadOnly(modeStack),
|
||||
|
||||
--- Get current mode (nil if all windows are closed), equivalent to `I.UI.modes[#I.UI.modes]`
|
||||
-- @function [parent=#UI] getMode
|
||||
-- @return #string
|
||||
getMode = function() return modeStack[#modeStack] end,
|
||||
|
||||
--- Drop all active modes and set mode.
|
||||
-- @function [parent=#UI] setMode
|
||||
-- @param #string mode (optional) New mode
|
||||
-- @param #table options (optional) Table with keys 'windows' and/or 'target' (see example).
|
||||
-- @usage I.UI.setMode() -- drop all modes
|
||||
-- @usage I.UI.setMode('Interface') -- drop all modes and open interface
|
||||
-- @usage -- Drop all modes, open interface, but show only the map window.
|
||||
-- I.UI.setMode('Interface', {windows = {'Map'}})
|
||||
setMode = setMode,
|
||||
|
||||
--- Add mode to stack without dropping other active modes.
|
||||
-- @function [parent=#UI] addMode
|
||||
-- @param #string mode New mode
|
||||
-- @param #table options (optional) Table with keys 'windows' and/or 'target' (see example).
|
||||
-- @usage I.UI.addMode('Journal') -- open journal without dropping active modes.
|
||||
-- @usage -- Open barter with an NPC
|
||||
-- I.UI.addMode('Barter', {target = actor})
|
||||
addMode = addMode,
|
||||
|
||||
--- Remove the specified mode from active modes.
|
||||
-- @function [parent=#UI] removeMode
|
||||
-- @param #string mode Mode to drop
|
||||
removeMode = removeMode,
|
||||
|
||||
-- TODO
|
||||
-- registerHudElement = function(name, showFn, hideFn) end,
|
||||
-- showHud = function(bool) end,
|
||||
-- isHudVisible = function() end,
|
||||
-- showHudElement = function(name, bool) end,
|
||||
-- hudElements, -- map from element name to its visibility
|
||||
},
|
||||
engineHandlers = {
|
||||
_onUiModeChanged = onUiModeChanged,
|
||||
},
|
||||
eventHandlers = {
|
||||
UiModeChanged = function() end,
|
||||
},
|
||||
}
|
@ -17,6 +17,9 @@
|
||||
---
|
||||
-- @field [parent=#interfaces] scripts.omw.settings.player#scripts.omw.settings.player Settings
|
||||
|
||||
---
|
||||
-- @field [parent=#interfaces] scripts.omw.ui#scripts.omw.ui UI
|
||||
|
||||
---
|
||||
-- @function [parent=#interfaces] __index
|
||||
-- @param #interfaces self
|
||||
|
Loading…
x
Reference in New Issue
Block a user