mirror of
https://github.com/OpenMW/openmw.git
synced 2025-04-02 05:06:40 +00:00
Merge branch 'ui' into 'master'
Control GUI from Lua See merge request OpenMW/openmw!3236
This commit is contained in:
commit
86f15fa194
68 changed files with 656 additions and 123 deletions
|
@ -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;
|
||||
|
|
|
@ -119,9 +119,9 @@ namespace MWBase
|
|||
|
||||
virtual void pushGuiMode(MWGui::GuiMode mode, const MWWorld::Ptr& arg) = 0;
|
||||
virtual void pushGuiMode(MWGui::GuiMode mode) = 0;
|
||||
virtual void popGuiMode(bool noSound = false) = 0;
|
||||
virtual void popGuiMode() = 0;
|
||||
|
||||
virtual void removeGuiMode(MWGui::GuiMode mode, bool noSound = false) = 0;
|
||||
virtual void removeGuiMode(MWGui::GuiMode mode) = 0;
|
||||
///< can be anywhere in the stack
|
||||
|
||||
virtual void goToJail(int days) = 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -580,7 +580,7 @@ namespace MWDialogue
|
|||
|
||||
void DialogueManager::applyBarterDispositionChange(int delta)
|
||||
{
|
||||
if (mActor.getClass().isNpc())
|
||||
if (!mActor.isEmpty() && mActor.getClass().isNpc())
|
||||
{
|
||||
updateOriginalDisposition();
|
||||
mCurrentDisposition += delta;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -83,6 +83,8 @@ namespace MWGui
|
|||
|
||||
void BookWindow::setPtr(const MWWorld::Ptr& book)
|
||||
{
|
||||
if (book.isEmpty() || book.getType() != ESM::REC_BOOK)
|
||||
throw std::runtime_error("Invalid argument in BookWindow::setPtr");
|
||||
mBook = book;
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -121,6 +121,8 @@ namespace MWGui
|
|||
|
||||
void CompanionWindow::setPtr(const MWWorld::Ptr& npc)
|
||||
{
|
||||
if (npc.isEmpty() || npc.getType() != ESM::REC_NPC_)
|
||||
throw std::runtime_error("Invalid argument in CompanionWindow::setPtr");
|
||||
mPtr = npc;
|
||||
updateEncumbranceBar();
|
||||
auto model = std::make_unique<CompanionItemModel>(npc);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -124,6 +124,8 @@ namespace MWGui
|
|||
|
||||
void ContainerWindow::setPtr(const MWWorld::Ptr& container)
|
||||
{
|
||||
if (container.isEmpty() || (container.getType() != ESM::REC_CONT && !container.getClass().isActor()))
|
||||
throw std::runtime_error("Invalid argument in ContainerWindow::setPtr");
|
||||
bool lootAnyway = mTreatNextOpenAsLoot;
|
||||
mTreatNextOpenAsLoot = false;
|
||||
mPtr = container;
|
||||
|
@ -191,6 +193,8 @@ namespace MWGui
|
|||
|
||||
void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
if (!mModel)
|
||||
return;
|
||||
if (mDragAndDrop != nullptr && mDragAndDrop->mIsOnDragAndDrop)
|
||||
return;
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace MWGui
|
|||
|
||||
void treatNextOpenAsLoot() { mTreatNextOpenAsLoot = true; }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Container"; }
|
||||
|
||||
private:
|
||||
DragAndDrop* mDragAndDrop;
|
||||
|
||||
|
|
|
@ -446,7 +446,7 @@ namespace MWGui
|
|||
|
||||
void DialogueWindow::setPtr(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (!actor.getClass().isActor())
|
||||
if (actor.isEmpty() || !actor.getClass().isActor())
|
||||
{
|
||||
Log(Debug::Warning) << "Warning: can not talk with non-actor object.";
|
||||
return;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -139,6 +139,9 @@ namespace MWGui
|
|||
|
||||
void EnchantingDialog::setPtr(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (ptr.isEmpty() || (ptr.getType() != ESM::REC_MISC && !ptr.getClass().isActor()))
|
||||
throw std::runtime_error("Invalid argument in EnchantingDialog::setPtr");
|
||||
|
||||
mName->setCaption({});
|
||||
|
||||
if (ptr.getClass().isActor())
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -226,10 +226,6 @@ namespace
|
|||
|
||||
void onOpen() override
|
||||
{
|
||||
if (!MWBase::Environment::get().getWindowManager()->getJournalAllowed())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
}
|
||||
mModel->load();
|
||||
|
||||
setBookMode();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -32,6 +32,8 @@ namespace MWGui
|
|||
|
||||
void MerchantRepair::setPtr(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (actor.isEmpty() || !actor.getClass().isActor())
|
||||
throw std::runtime_error("Invalid argument in MerchantRepair::setPtr");
|
||||
mActor = actor;
|
||||
|
||||
while (mList->getChildCount())
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -56,6 +56,9 @@ namespace MWGui
|
|||
|
||||
void Recharge::setPtr(const MWWorld::Ptr& item)
|
||||
{
|
||||
if (item.isEmpty() || !item.getClass().isItem(item))
|
||||
throw std::runtime_error("Invalid argument in Recharge::setPtr");
|
||||
|
||||
mGemIcon->setItem(item);
|
||||
mGemIcon->setUserString("ToolTipType", "ItemPtr");
|
||||
mGemIcon->setUserData(MWWorld::Ptr(item));
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace MWGui
|
|||
|
||||
void setPtr(const MWWorld::Ptr& gem) override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Recharge"; }
|
||||
|
||||
protected:
|
||||
ItemChargeView* mBox;
|
||||
|
||||
|
|
|
@ -56,6 +56,9 @@ namespace MWGui
|
|||
|
||||
void Repair::setPtr(const MWWorld::Ptr& item)
|
||||
{
|
||||
if (item.isEmpty() || !item.getClass().isItem(item))
|
||||
throw std::runtime_error("Invalid argument in Repair::setPtr");
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Item Repair Up"));
|
||||
|
||||
mRepair.setTool(item);
|
||||
|
|
|
@ -23,6 +23,8 @@ namespace MWGui
|
|||
|
||||
void setPtr(const MWWorld::Ptr& item) override;
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Repair"; }
|
||||
|
||||
protected:
|
||||
ItemChargeView* mRepairBox;
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ namespace MWGui
|
|||
|
||||
void ScrollWindow::setPtr(const MWWorld::Ptr& scroll)
|
||||
{
|
||||
if (scroll.isEmpty() || scroll.getType() != ESM::REC_BOOK)
|
||||
throw std::runtime_error("Invalid argument in ScrollWindow::setPtr");
|
||||
mScroll = scroll;
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
|
@ -105,6 +107,6 @@ namespace MWGui
|
|||
MWWorld::ActionTake take(mScroll);
|
||||
take.execute(MWMechanics::getPlayer());
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll, true);
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -88,6 +88,9 @@ namespace MWGui
|
|||
|
||||
void SpellBuyingWindow::setPtr(const MWWorld::Ptr& actor, int startOffset)
|
||||
{
|
||||
if (actor.isEmpty() || !actor.getClass().isActor())
|
||||
throw std::runtime_error("Invalid argument in SpellBuyingWindow::setPtr");
|
||||
|
||||
center();
|
||||
mPtr = actor;
|
||||
clearSpells();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -364,6 +364,9 @@ namespace MWGui
|
|||
|
||||
void SpellCreationDialog::setPtr(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (actor.isEmpty() || !actor.getClass().isActor())
|
||||
throw std::runtime_error("Invalid argument in SpellCreationDialog::setPtr");
|
||||
|
||||
mPtr = actor;
|
||||
mNameEdit->setCaption({});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -102,6 +102,8 @@ namespace MWGui
|
|||
|
||||
void TradeWindow::setPtr(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (actor.isEmpty() || !actor.getClass().isActor())
|
||||
throw std::runtime_error("Invalid argument in TradeWindow::setPtr");
|
||||
mPtr = actor;
|
||||
|
||||
mCurrentBalance = 0;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -53,6 +53,8 @@ namespace MWGui
|
|||
|
||||
void TrainingWindow::setPtr(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (actor.isEmpty() || !actor.getClass().isActor())
|
||||
throw std::runtime_error("Invalid argument in TrainingWindow::setPtr");
|
||||
mPtr = actor;
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
|
|
|
@ -31,6 +31,8 @@ namespace MWGui
|
|||
|
||||
void clear() override { resetReference(); }
|
||||
|
||||
std::string_view getWindowIdForLua() const override { return "Training"; }
|
||||
|
||||
protected:
|
||||
void onReferenceUnavailable() override;
|
||||
|
||||
|
|
|
@ -112,6 +112,9 @@ namespace MWGui
|
|||
|
||||
void TravelWindow::setPtr(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (actor.isEmpty() || !actor.getClass().isActor())
|
||||
throw std::runtime_error("Invalid argument in TravelWindow::setPtr");
|
||||
|
||||
center();
|
||||
mPtr = actor;
|
||||
clearDestinations();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -360,8 +360,6 @@ namespace MWGui
|
|||
bool questList = mResourceSystem->getVFS()->exists("textures/tx_menubook_options_over.dds");
|
||||
auto journal = JournalWindow::create(JournalViewModel::create(), questList, mEncoding);
|
||||
mGuiModeStates[GM_Journal] = GuiModeState(journal.get());
|
||||
mGuiModeStates[GM_Journal].mCloseSound = ESM::RefId::stringRefId("book close");
|
||||
mGuiModeStates[GM_Journal].mOpenSound = ESM::RefId::stringRefId("book open");
|
||||
mWindows.push_back(std::move(journal));
|
||||
|
||||
mMessageBoxManager = std::make_unique<MessageBoxManager>(
|
||||
|
@ -398,15 +396,11 @@ namespace MWGui
|
|||
mScrollWindow = scrollWindow.get();
|
||||
mWindows.push_back(std::move(scrollWindow));
|
||||
mGuiModeStates[GM_Scroll] = GuiModeState(mScrollWindow);
|
||||
mGuiModeStates[GM_Scroll].mOpenSound = ESM::RefId::stringRefId("scroll");
|
||||
mGuiModeStates[GM_Scroll].mCloseSound = ESM::RefId::stringRefId("scroll");
|
||||
|
||||
auto bookWindow = std::make_unique<BookWindow>();
|
||||
mBookWindow = bookWindow.get();
|
||||
mWindows.push_back(std::move(bookWindow));
|
||||
mGuiModeStates[GM_Book] = GuiModeState(mBookWindow);
|
||||
mGuiModeStates[GM_Book].mOpenSound = ESM::RefId::stringRefId("book open");
|
||||
mGuiModeStates[GM_Book].mCloseSound = ESM::RefId::stringRefId("book close");
|
||||
|
||||
auto countDialog = std::make_unique<CountDialog>();
|
||||
mCountDialog = countDialog.get();
|
||||
|
@ -525,6 +519,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)
|
||||
|
@ -1267,16 +1268,25 @@ namespace MWGui
|
|||
mGuiModes.push_back(mode);
|
||||
|
||||
mGuiModeStates[mode].update(true);
|
||||
playSound(mGuiModeStates[mode].mOpenSound);
|
||||
}
|
||||
if (force)
|
||||
mContainerWindow->treatNextOpenAsLoot();
|
||||
for (WindowBase* window : mGuiModeStates[mode].mWindows)
|
||||
window->setPtr(arg);
|
||||
|
||||
try
|
||||
{
|
||||
for (WindowBase* window : mGuiModeStates[mode].mWindows)
|
||||
window->setPtr(arg);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
popGuiMode();
|
||||
throw;
|
||||
}
|
||||
|
||||
mKeyboardNavigation->restoreFocus(mode);
|
||||
|
||||
updateVisible();
|
||||
MWBase::Environment::get().getLuaManager()->uiModeChanged(arg);
|
||||
}
|
||||
|
||||
void WindowManager::setCullMask(uint32_t mask)
|
||||
|
@ -1294,7 +1304,7 @@ namespace MWGui
|
|||
return mViewer->getCamera()->getCullMask();
|
||||
}
|
||||
|
||||
void WindowManager::popGuiMode(bool noSound)
|
||||
void WindowManager::popGuiMode()
|
||||
{
|
||||
if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop)
|
||||
{
|
||||
|
@ -1307,8 +1317,7 @@ namespace MWGui
|
|||
mKeyboardNavigation->saveFocus(mode);
|
||||
mGuiModes.pop_back();
|
||||
mGuiModeStates[mode].update(false);
|
||||
if (!noSound)
|
||||
playSound(mGuiModeStates[mode].mCloseSound);
|
||||
MWBase::Environment::get().getLuaManager()->uiModeChanged(MWWorld::Ptr());
|
||||
}
|
||||
|
||||
if (!mGuiModes.empty())
|
||||
|
@ -1325,11 +1334,11 @@ namespace MWGui
|
|||
mConsole->onOpen();
|
||||
}
|
||||
|
||||
void WindowManager::removeGuiMode(GuiMode mode, bool noSound)
|
||||
void WindowManager::removeGuiMode(GuiMode mode)
|
||||
{
|
||||
if (!mGuiModes.empty() && mGuiModes.back() == mode)
|
||||
{
|
||||
popGuiMode(noSound);
|
||||
popGuiMode();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,8 +149,8 @@ namespace MWGui
|
|||
|
||||
void pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg) override;
|
||||
void pushGuiMode(GuiMode mode) override;
|
||||
void popGuiMode(bool noSound = false) override;
|
||||
void removeGuiMode(GuiMode mode, bool noSound = false) override; ///< can be anywhere in the stack
|
||||
void popGuiMode() override;
|
||||
void removeGuiMode(GuiMode mode) override; ///< can be anywhere in the stack
|
||||
|
||||
void goToJail(int days) override;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -479,9 +488,6 @@ namespace MWGui
|
|||
void update(bool visible);
|
||||
|
||||
std::vector<WindowBase*> mWindows;
|
||||
|
||||
ESM::RefId mCloseSound;
|
||||
ESM::RefId mOpenSound;
|
||||
};
|
||||
// Defines the windows that should be shown in a particular GUI mode.
|
||||
std::map<GuiMode, GuiModeState> mGuiModeStates;
|
||||
|
|
|
@ -71,9 +71,6 @@ namespace MWInput
|
|||
case A_Screenshot:
|
||||
screenshot();
|
||||
break;
|
||||
case A_Inventory:
|
||||
toggleInventory();
|
||||
break;
|
||||
case A_Console:
|
||||
toggleConsole();
|
||||
break;
|
||||
|
@ -87,9 +84,6 @@ namespace MWInput
|
|||
case A_MoveBackward:
|
||||
handleGuiArrowKey(action);
|
||||
break;
|
||||
case A_Journal:
|
||||
toggleJournal();
|
||||
break;
|
||||
case A_Rest:
|
||||
rest();
|
||||
break;
|
||||
|
@ -123,9 +117,6 @@ namespace MWInput
|
|||
case A_QuickKey10:
|
||||
quickKey(10);
|
||||
break;
|
||||
case A_QuickKeysMenu:
|
||||
showQuickKeysMenu();
|
||||
break;
|
||||
case A_ToggleHUD:
|
||||
windowManager->toggleHud();
|
||||
break;
|
||||
|
@ -157,6 +148,11 @@ namespace MWInput
|
|||
if (checkAllowedToUseItems() && windowManager->isAllowed(MWGui::GW_Inventory))
|
||||
MWBase::Environment::get().getWindowManager()->cycleWeapon(true);
|
||||
break;
|
||||
case A_Inventory:
|
||||
case A_Journal:
|
||||
case A_QuickKeysMenu:
|
||||
// Handled in Lua
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,30 +244,6 @@ namespace MWInput
|
|||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Rest); // Open rest GUI
|
||||
}
|
||||
|
||||
void ActionManager::toggleInventory()
|
||||
{
|
||||
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
|
||||
return;
|
||||
|
||||
if (MyGUI::InputManager::getInstance().isModalAny())
|
||||
return;
|
||||
|
||||
if (MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
||||
return;
|
||||
|
||||
// Toggle between game mode and inventory mode
|
||||
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Inventory);
|
||||
else
|
||||
{
|
||||
MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();
|
||||
if (mode == MWGui::GM_Inventory || mode == MWGui::GM_Container)
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
}
|
||||
|
||||
// .. but don't touch any other mode, except container.
|
||||
}
|
||||
|
||||
void ActionManager::toggleConsole()
|
||||
{
|
||||
if (MyGUI::InputManager::getInstance().isModalAny())
|
||||
|
@ -280,25 +252,6 @@ namespace MWInput
|
|||
MWBase::Environment::get().getWindowManager()->toggleConsole();
|
||||
}
|
||||
|
||||
void ActionManager::toggleJournal()
|
||||
{
|
||||
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
|
||||
return;
|
||||
if (MyGUI::InputManager::getInstance().isModalAny())
|
||||
return;
|
||||
|
||||
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();
|
||||
if (windowManager->getMode() != MWGui::GM_Journal && windowManager->getMode() != MWGui::GM_MainMenu
|
||||
&& windowManager->getMode() != MWGui::GM_Settings && windowManager->getJournalAllowed())
|
||||
{
|
||||
windowManager->pushGuiMode(MWGui::GM_Journal);
|
||||
}
|
||||
else if (windowManager->containsMode(MWGui::GM_Journal))
|
||||
{
|
||||
windowManager->removeGuiMode(MWGui::GM_Journal);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionManager::quickKey(int index)
|
||||
{
|
||||
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")
|
||||
|
@ -315,23 +268,6 @@ namespace MWInput
|
|||
MWBase::Environment::get().getWindowManager()->activateQuickKey(index);
|
||||
}
|
||||
|
||||
void ActionManager::showQuickKeysMenu()
|
||||
{
|
||||
if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_QuickKeysMenu)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (MWBase::Environment::get().getWorld()->getGlobalFloat(MWWorld::Globals::sCharGenState) != -1)
|
||||
return;
|
||||
|
||||
if (!checkAllowedToUseItems())
|
||||
return;
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_QuickKeysMenu);
|
||||
}
|
||||
|
||||
void ActionManager::activate()
|
||||
{
|
||||
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
|
|
|
@ -28,17 +28,14 @@ namespace MWInput
|
|||
bool checkAllowedToUseItems() const;
|
||||
|
||||
void toggleMainMenu();
|
||||
void toggleInventory();
|
||||
void toggleConsole();
|
||||
void screenshot();
|
||||
void toggleJournal();
|
||||
void activate();
|
||||
void rest();
|
||||
void quickLoad();
|
||||
void quickSave();
|
||||
|
||||
void quickKey(int index);
|
||||
void showQuickKeysMenu();
|
||||
|
||||
void resetIdleTime();
|
||||
float getIdleTime() const { return mTimeIdle; }
|
||||
|
|
|
@ -128,7 +128,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();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <filesystem>
|
||||
|
||||
#include <MyGUI_InputManager.h>
|
||||
#include <osg/Stats>
|
||||
|
||||
#include "sol/state_view.hpp"
|
||||
|
@ -311,6 +312,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.
|
||||
|
@ -343,6 +353,15 @@ namespace MWLua
|
|||
}
|
||||
}
|
||||
|
||||
void LuaManager::inputEvent(const InputEvent& event)
|
||||
{
|
||||
if (!MyGUI::InputManager::getInstance().isModalAny()
|
||||
&& !MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
||||
{
|
||||
mInputEvents.push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
MWBase::LuaManager::ActorControls* LuaManager::getActorControls(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace MWLua
|
|||
void gameLoaded() override;
|
||||
void objectAddedToScene(const MWWorld::Ptr& ptr) override;
|
||||
void objectRemovedFromScene(const MWWorld::Ptr& ptr) override;
|
||||
void inputEvent(const InputEvent& event) override { mInputEvents.push_back(event); }
|
||||
void inputEvent(const InputEvent& event) override;
|
||||
void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) override
|
||||
{
|
||||
mEngineEvents.addToQueue(EngineEvents::OnConsume{ getId(actor), getId(consumable) });
|
||||
|
@ -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" };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <apps/openmw/mwbase/world.hpp>
|
||||
#include <apps/openmw/mwmechanics/npcstats.hpp>
|
||||
#include <apps/openmw/mwworld/class.hpp>
|
||||
#include <apps/openmw/mwworld/globals.hpp>
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
@ -126,6 +127,9 @@ namespace MWLua
|
|||
const MWWorld::Class& cls = o.ptr().getClass();
|
||||
return cls.getNpcStats(o.ptr()).getBounty();
|
||||
};
|
||||
player["isCharGenFinished"] = [](const Object&) -> bool {
|
||||
return MWBase::Environment::get().getWorld()->getGlobalFloat(MWWorld::Globals::sCharGenState) == -1;
|
||||
};
|
||||
addPlayerQuestBindings(player, context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -246,6 +246,9 @@ namespace LuaUtil
|
|||
local nextFn, t, firstKey = ipairs(getmetatable(v).t)
|
||||
return function(_, k) return nextFn(t, k) end, v, firstKey
|
||||
end
|
||||
function lenForReadOnly(v)
|
||||
return #getmetatable(v).t
|
||||
end
|
||||
local function nextForArray(array, index)
|
||||
index = (index or 0) + 1
|
||||
if index <= #array then
|
||||
|
@ -300,6 +303,7 @@ namespace LuaUtil
|
|||
meta["__index"] = table;
|
||||
meta["__pairs"] = lua["pairsForReadOnly"];
|
||||
meta["__ipairs"] = lua["ipairsForReadOnly"];
|
||||
meta["__len"] = lua["lenForReadOnly"];
|
||||
|
||||
lua_newuserdata(luaState, 0);
|
||||
sol::stack::push(luaState, meta);
|
||||
|
|
|
@ -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 ``oldMode, ``newMode`` (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 from', data.oldMode , 'to', data.newMode, '('..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
|
||||
|
|
|
@ -47,6 +47,7 @@ local startAttack = false
|
|||
local autoMove = false
|
||||
local movementControlsOverridden = false
|
||||
local combatControlsOverridden = false
|
||||
local uiControlsOverridden = false
|
||||
|
||||
local function processMovement()
|
||||
local controllerMovement = -input.getAxisValue(input.CONTROLLER_AXIS.MoveForwardBackward)
|
||||
|
@ -123,8 +124,48 @@ local function onFrame(dt)
|
|||
startAttack = false
|
||||
end
|
||||
|
||||
local function checkNotWerewolf()
|
||||
if Player.isWerewolf(self) then
|
||||
ui.showMessage(core.getGMST('sWerewolfRefusal'))
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function isJournalAllowed()
|
||||
-- During chargen journal is not allowed until magic window is allowed
|
||||
return I.UI.getWindowsForMode(I.UI.MODE.Interface)[I.UI.WINDOW.Magic]
|
||||
end
|
||||
|
||||
local function onInputAction(action)
|
||||
if core.isWorldPaused() or not input.getControlSwitch(input.CONTROL_SWITCH.Controls) then
|
||||
if not input.getControlSwitch(input.CONTROL_SWITCH.Controls) then
|
||||
return
|
||||
end
|
||||
|
||||
if not uiControlsOverridden then
|
||||
if action == input.ACTION.Inventory then
|
||||
if I.UI.getMode() == nil then
|
||||
I.UI.setMode(I.UI.MODE.Interface)
|
||||
elseif I.UI.getMode() == I.UI.MODE.Interface or I.UI.getMode() == I.UI.MODE.Container then
|
||||
I.UI.removeMode(I.UI.getMode())
|
||||
end
|
||||
elseif action == input.ACTION.Journal then
|
||||
if I.UI.getMode() == I.UI.MODE.Journal then
|
||||
I.UI.removeMode(I.UI.MODE.Journal)
|
||||
elseif isJournalAllowed() then
|
||||
I.UI.addMode(I.UI.MODE.Journal)
|
||||
end
|
||||
elseif action == input.ACTION.QuickKeysMenu then
|
||||
if I.UI.getMode() == I.UI.MODE.QuickKeysMenu then
|
||||
I.UI.removeMode(I.UI.MODE.QuickKeysMenu)
|
||||
elseif checkNotWerewolf() and Player.isCharGenFinished(self) then
|
||||
I.UI.addMode(I.UI.MODE.QuickKeysMenu)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if core.isWorldPaused() then
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -144,9 +185,7 @@ local function onInputAction(action)
|
|||
if Actor.stance(self) == Actor.STANCE.Spell then
|
||||
Actor.setStance(self, Actor.STANCE.Nothing)
|
||||
elseif input.getControlSwitch(input.CONTROL_SWITCH.Magic) then
|
||||
if Player.isWerewolf(self) then
|
||||
ui.showMessage(core.getGMST('sWerewolfRefusal'))
|
||||
else
|
||||
if checkNotWerewolf() then
|
||||
Actor.setStance(self, Actor.STANCE.Spell)
|
||||
end
|
||||
end
|
||||
|
@ -171,19 +210,24 @@ return {
|
|||
interface = {
|
||||
--- Interface version
|
||||
-- @field [parent=#Controls] #number version
|
||||
version = 0,
|
||||
version = 1,
|
||||
|
||||
--- When set to true then the movement controls including jump and sneak are not processed and can be handled by another script.
|
||||
-- If movement should be dissallowed completely, consider to use `input.setControlSwitch` instead.
|
||||
-- If movement should be disallowed completely, consider to use `input.setControlSwitch` instead.
|
||||
-- @function [parent=#Controls] overrideMovementControls
|
||||
-- @param #boolean value
|
||||
overrideMovementControls = function(v) movementControlsOverridden = v end,
|
||||
|
||||
--- When set to true then the controls "attack", "toggle spell", "toggle weapon" are not processed and can be handled by another script.
|
||||
-- If combat should be dissallowed completely, consider to use `input.setControlSwitch` instead.
|
||||
-- If combat should be disallowed completely, consider to use `input.setControlSwitch` instead.
|
||||
-- @function [parent=#Controls] overrideCombatControls
|
||||
-- @param #boolean value
|
||||
overrideCombatControls = function(v) combatControlsOverridden = v end,
|
||||
|
||||
--- When set to true then the controls "open inventory", "open journal" and so on are not processed and can be handled by another script.
|
||||
-- @function [parent=#Controls] overrideUiControls
|
||||
-- @param #boolean value
|
||||
overrideUiControls = function(v) uiControlsOverridden = v end,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
220
files/data/scripts/omw/ui.lua
Normal file
220
files/data/scripts/omw/ui.lua
Normal file
|
@ -0,0 +1,220 @@
|
|||
local ui = require('openmw.ui')
|
||||
local util = require('openmw.util')
|
||||
local self = require('openmw.self')
|
||||
local ambient = require('openmw.ambient')
|
||||
|
||||
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)
|
||||
local function impl()
|
||||
updateHidden(mode, options)
|
||||
ui._setUiModeStack({mode}, options and options.target)
|
||||
end
|
||||
if mode then
|
||||
if not pcall(impl) then
|
||||
error('Invalid mode: ' .. tostring(mode))
|
||||
end
|
||||
else
|
||||
ui._setUiModeStack({})
|
||||
end
|
||||
end
|
||||
|
||||
local function addMode(mode, options)
|
||||
local function impl()
|
||||
updateHidden(mode, options)
|
||||
ui._setUiModeStack(modeStack, options and options.target)
|
||||
end
|
||||
modeStack[#modeStack + 1] = mode
|
||||
if not pcall(impl) then
|
||||
modeStack[#modeStack] = nil
|
||||
error('Invalid mode: ' .. tostring(mode))
|
||||
end
|
||||
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 oldMode = nil
|
||||
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', {oldMode = oldMode, newMode = mode, arg = arg})
|
||||
oldMode = mode
|
||||
end
|
||||
|
||||
local function onUiModeChangedEvent(data)
|
||||
if data.oldMode == data.newMode then
|
||||
return
|
||||
end
|
||||
-- Sounds are processed in the event handler rather than in engine handler
|
||||
-- in order to allow them to be overridden in mods.
|
||||
if data.newMode == MODE.Journal or data.newMode == MODE.Book then
|
||||
ambient.playSound('book open', {scale = false})
|
||||
elseif data.oldMode == MODE.Journal or data.oldMode == MODE.Book then
|
||||
if not ambient.isSoundPlaying('item book up') then
|
||||
ambient.playSound('book close', {scale = false})
|
||||
end
|
||||
elseif data.newMode == MODE.Scroll or data.oldMode == MODE.Scroll then
|
||||
if not ambient.isSoundPlaying('item book up') then
|
||||
ambient.playSound('scroll', {scale = false})
|
||||
end
|
||||
end
|
||||
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 = onUiModeChangedEvent,
|
||||
},
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -751,6 +751,12 @@
|
|||
-- @param openmw.core#GameObject player
|
||||
-- @return #number
|
||||
|
||||
---
|
||||
-- Whether the character generation for this player is finished.
|
||||
-- @function [parent=#Player] isCharGenFinished
|
||||
-- @param openmw.core#GameObject player
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Returns a list containing quests @{#PlayerQuest} for the specified player, indexed by quest ID.
|
||||
-- @function [parent=#Player] quests
|
||||
|
|
Loading…
Reference in a new issue