From 17938cdb7e475d83514cf91b9e20d99f20d3dd39 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 1 Apr 2025 01:25:08 +0300 Subject: [PATCH 01/32] Make the crosshair smaller --- files/data/mygui/openmw_hud.layout | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/files/data/mygui/openmw_hud.layout b/files/data/mygui/openmw_hud.layout index a189075f44..78b8ff1aed 100644 --- a/files/data/mygui/openmw_hud.layout +++ b/files/data/mygui/openmw_hud.layout @@ -108,8 +108,7 @@ - - + @@ -131,4 +130,4 @@ - \ No newline at end of file + From 0547f09bf09e6e4258b39bd56d189a8ab6458200 Mon Sep 17 00:00:00 2001 From: Kindi Date: Tue, 1 Apr 2025 13:03:11 +0800 Subject: [PATCH 02/32] update container window when item is added or removed --- apps/openmw/mwgui/container.cpp | 6 ++++++ apps/openmw/mwgui/container.hpp | 9 ++++++++- apps/openmw/mwgui/draganddrop.cpp | 2 ++ apps/openmw/mwgui/inventorywindow.cpp | 2 ++ apps/openmw/mwgui/inventorywindow.hpp | 6 +++++- 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 6ab2c862d4..b0c3197519 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -160,6 +160,12 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); + mPtr.getClass().getContainerStore(mPtr).setContListener(this); + } + + void ContainerWindow::updateItemView() + { + mItemView->update(); } void ContainerWindow::resetReference() diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 555fa8e1ae..4898a0eb1c 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -6,6 +6,8 @@ #include "itemmodel.hpp" +#include "../mwworld/containerstore.hpp" + namespace MyGUI { class Gui; @@ -21,7 +23,7 @@ namespace MWGui namespace MWGui { - class ContainerWindow : public WindowBase, public ReferenceInterface + class ContainerWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener { public: ContainerWindow(DragAndDrop* dragAndDrop); @@ -38,6 +40,11 @@ namespace MWGui void treatNextOpenAsLoot() { mTreatNextOpenAsLoot = true; } + void updateItemView(); + + void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + std::string_view getWindowIdForLua() const override { return "Container"; } private: diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index 0fa2cc4e21..36cfe649d6 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -91,6 +91,8 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setDragDrop(true); mIsOnDragAndDrop = true; + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); } void DragAndDrop::drop(ItemModel* targetModel, ItemView* targetView) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index a773b4635b..27dab07d34 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -145,6 +145,8 @@ namespace MWGui auto tradeModel = std::make_unique(std::make_unique(mPtr), MWWorld::Ptr()); mTradeModel = tradeModel.get(); + mPtr.getClass().getInventoryStore(mPtr).setContListener(this); + if (mSortModel) // reuse existing SortModel when possible to keep previous category/filter settings mSortModel->setSourceModel(std::move(tradeModel)); else diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 9fc77ceec5..1dd12aa550 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -5,6 +5,7 @@ #include "windowpinnablebase.hpp" #include "../mwrender/characterpreview.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwworld/ptr.hpp" namespace osg @@ -30,7 +31,7 @@ namespace MWGui class DragAndDrop; class ItemModel; - class InventoryWindow : public WindowPinnableBase + class InventoryWindow : public WindowPinnableBase, public MWWorld::ContainerStoreListener { public: InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem); @@ -62,6 +63,9 @@ namespace MWGui void setGuiMode(GuiMode mode); + void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + /// Cycle to previous/next weapon void cycle(bool next); From 15ceee4b1a35842c80bb683fe7e714604dac3996 Mon Sep 17 00:00:00 2001 From: Kindi Date: Thu, 3 Apr 2025 11:26:56 +0800 Subject: [PATCH 03/32] also update companion window and tradewindow --- apps/openmw/mwgui/companionwindow.cpp | 7 +++++++ apps/openmw/mwgui/companionwindow.hpp | 9 ++++++++- apps/openmw/mwgui/container.cpp | 11 ++++++----- apps/openmw/mwgui/tradewindow.cpp | 8 ++++++++ apps/openmw/mwgui/tradewindow.hpp | 9 ++++++++- 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 240198eddc..731403f3a3 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -134,6 +134,8 @@ namespace MWGui mItemView->resetScrollBars(); setTitle(actor.getClass().getName(actor)); + + mPtr.getClass().getContainerStore(mPtr).setContListener(this); } void CompanionWindow::onFrame(float dt) @@ -202,4 +204,9 @@ namespace MWGui mSortModel = nullptr; } + void CompanionWindow::updateItemView() + { + mItemView->update(); + } + } diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 97f3a0072e..6f8452997f 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -4,6 +4,8 @@ #include "referenceinterface.hpp" #include "windowbase.hpp" +#include "../mwworld/containerstore.hpp" + namespace MWGui { namespace Widgets @@ -17,7 +19,7 @@ namespace MWGui class SortFilterItemModel; class CompanionItemModel; - class CompanionWindow : public WindowBase, public ReferenceInterface + class CompanionWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener { public: CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager); @@ -30,6 +32,11 @@ namespace MWGui void onFrame(float dt) override; void clear() override { resetReference(); } + void updateItemView(); + + void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + std::string_view getWindowIdForLua() const override { return "Companion"; } private: diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b0c3197519..bb0f90ddc9 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -160,12 +160,8 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); - mPtr.getClass().getContainerStore(mPtr).setContListener(this); - } - void ContainerWindow::updateItemView() - { - mItemView->update(); + mPtr.getClass().getContainerStore(mPtr).setContListener(this); } void ContainerWindow::resetReference() @@ -326,4 +322,9 @@ namespace MWGui if (mModel && mModel->usesContainer(ptr)) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } + + void ContainerWindow::updateItemView() + { + mItemView->update(); + } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index ba752303d2..ad5165c6ae 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -201,6 +201,9 @@ namespace MWGui onFilterChanged(mFilterAll); mFilterEdit->setCaption({}); + + for (const auto& source : itemSources) + source.getClass().getContainerStore(source).setContListener(this); } void TradeWindow::onFrame(float dt) @@ -643,4 +646,9 @@ namespace MWGui if (mTradeModel && mTradeModel->usesContainer(ptr)) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } + + void TradeWindow::updateItemView() + { + mItemView->update(); + } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 33c39cb269..8614655c47 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -4,6 +4,8 @@ #include "referenceinterface.hpp" #include "windowbase.hpp" +#include "../mwworld/containerstore.hpp" + namespace Gui { class NumericEditBox; @@ -20,7 +22,7 @@ namespace MWGui class SortFilterItemModel; class TradeItemModel; - class TradeWindow : public WindowBase, public ReferenceInterface + class TradeWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener { public: TradeWindow(); @@ -42,6 +44,11 @@ namespace MWGui void onDeleteCustomData(const MWWorld::Ptr& ptr) override; + void updateItemView(); + + void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + typedef MyGUI::delegates::MultiDelegate<> EventHandle_TradeDone; EventHandle_TradeDone eventTradeDone; From a9870bbcde803890dfd2e42062787c2ca2b0bfef Mon Sep 17 00:00:00 2001 From: Kindi Date: Tue, 1 Apr 2025 13:03:11 +0800 Subject: [PATCH 04/32] update container window when item is added or removed --- apps/openmw/mwgui/container.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index bb0f90ddc9..b0c3197519 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -160,10 +160,14 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); - mPtr.getClass().getContainerStore(mPtr).setContListener(this); } + void ContainerWindow::updateItemView() + { + mItemView->update(); + } + void ContainerWindow::resetReference() { ReferenceInterface::resetReference(); @@ -322,9 +326,4 @@ namespace MWGui if (mModel && mModel->usesContainer(ptr)) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } - - void ContainerWindow::updateItemView() - { - mItemView->update(); - } } From bdf025a5325f85cdfa97720b01a5798505c0f221 Mon Sep 17 00:00:00 2001 From: Kindi Date: Thu, 3 Apr 2025 11:26:56 +0800 Subject: [PATCH 05/32] also update companion window and tradewindow --- apps/openmw/mwgui/container.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b0c3197519..bb0f90ddc9 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -160,12 +160,8 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); - mPtr.getClass().getContainerStore(mPtr).setContListener(this); - } - void ContainerWindow::updateItemView() - { - mItemView->update(); + mPtr.getClass().getContainerStore(mPtr).setContListener(this); } void ContainerWindow::resetReference() @@ -326,4 +322,9 @@ namespace MWGui if (mModel && mModel->usesContainer(ptr)) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } + + void ContainerWindow::updateItemView() + { + mItemView->update(); + } } From 4b94b6f678cd8c812ceaf78d59b02a708ba6d6dd Mon Sep 17 00:00:00 2001 From: Kindi Date: Wed, 23 Apr 2025 17:22:01 +0800 Subject: [PATCH 06/32] also update draganddrop and trade items --- apps/openmw/mwgui/draganddrop.cpp | 28 ++++++++++++++++++++------- apps/openmw/mwgui/draganddrop.hpp | 4 +++- apps/openmw/mwgui/inventorywindow.cpp | 15 ++++++++++++++ apps/openmw/mwgui/inventorywindow.hpp | 2 +- apps/openmw/mwgui/tradeitemmodel.cpp | 19 ++++++++++++++++++ apps/openmw/mwgui/tradeitemmodel.hpp | 3 +++ apps/openmw/mwgui/tradewindow.cpp | 9 +++++++++ apps/openmw/mwgui/tradewindow.hpp | 4 +++- 8 files changed, 74 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index 36cfe649d6..17ce5360c0 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -11,7 +11,6 @@ #include "controllers.hpp" #include "inventorywindow.hpp" #include "itemview.hpp" -#include "itemwidget.hpp" #include "sortfilteritemmodel.hpp" namespace MWGui @@ -72,19 +71,18 @@ namespace MWGui mSourceSortModel->addDragItem(mItem.mBase, count); } - ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget( + mDraggedWidget = MyGUI::Gui::getInstance().createWidget( "MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop"); Controllers::ControllerFollowMouse* controller = MyGUI::ControllerManager::getInstance() .createItem(Controllers::ControllerFollowMouse::getClassTypeName()) ->castType(); - MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller); + MyGUI::ControllerManager::getInstance().addItem(mDraggedWidget, controller); - mDraggedWidget = baseWidget; - baseWidget->setItem(mItem.mBase); - baseWidget->setNeedMouseFocus(false); - baseWidget->setCount(count); + mDraggedWidget->setItem(mItem.mBase); + mDraggedWidget->setNeedMouseFocus(false); + mDraggedWidget->setCount(count); sourceView->update(); @@ -126,6 +124,22 @@ namespace MWGui mSourceView->update(); } + void DragAndDrop::update() + { + if (mIsOnDragAndDrop) + { + int count = mItem.mBase.getCellRef().getCount(); + if (count < mDraggedCount) + { + mItem.mCount = count; + mDraggedCount = count; + mDraggedWidget->setCount(mDraggedCount); + mSourceSortModel->clearDragItems(); + mSourceSortModel->addDragItem(mItem.mBase, mDraggedCount); + } + } + } + void DragAndDrop::onFrame() { if (mIsOnDragAndDrop && mItem.mBase.getCellRef().getCount() == 0) diff --git a/apps/openmw/mwgui/draganddrop.hpp b/apps/openmw/mwgui/draganddrop.hpp index fab7f30d75..94da6cce5f 100644 --- a/apps/openmw/mwgui/draganddrop.hpp +++ b/apps/openmw/mwgui/draganddrop.hpp @@ -2,6 +2,7 @@ #define OPENMW_MWGUI_DRAGANDDROP_H #include "itemmodel.hpp" +#include "itemwidget.hpp" namespace MyGUI { @@ -18,7 +19,7 @@ namespace MWGui { public: bool mIsOnDragAndDrop; - MyGUI::Widget* mDraggedWidget; + ItemWidget* mDraggedWidget; ItemModel* mSourceModel; ItemView* mSourceView; SortFilterItemModel* mSourceSortModel; @@ -30,6 +31,7 @@ namespace MWGui void startDrag( int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); void drop(ItemModel* targetModel, ItemView* targetView); + void update(); void onFrame(); void finish(); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 27dab07d34..656f1a3db4 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -850,6 +850,21 @@ namespace MWGui mPreview->rebuild(); } + void InventoryWindow::itemRemoved(const MWWorld::ConstPtr& item, int count) + { + if (mDragAndDrop->mIsOnDragAndDrop && mDragAndDrop->mItem.mBase == item) + mDragAndDrop->update(); + + if (mTrading) + { + mTradeModel->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->getTradeModel()->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->updateItemView(); + } + + updateItemView(); + } + MyGUI::IntSize InventoryWindow::getPreviewViewportSize() const { const MyGUI::IntSize previewWindowSize = mAvatarImage->getSize(); diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 1dd12aa550..079e629673 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -64,7 +64,7 @@ namespace MWGui void setGuiMode(GuiMode mode); void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } - void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override; /// Cycle to previous/next weapon void cycle(bool next); diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index 50a55f5061..5df048d6db 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -113,6 +113,25 @@ namespace MWGui encumbrance = std::max(0.f, encumbrance); } + void TradeItemModel::updateBorrowed() + { + auto update = [](std::vector& list) { + for (auto it = list.begin(); it != list.end();) + { + int actualCount = it->mBase.getCellRef().getCount(); + if (actualCount < it->mCount) + it->mCount = actualCount; + if (it->mCount == 0) + it = list.erase(it); + else + ++it; + } + }; + + update(mBorrowedFromUs); + update(mBorrowedToUs); + } + void TradeItemModel::abort() { mBorrowedFromUs.clear(); diff --git a/apps/openmw/mwgui/tradeitemmodel.hpp b/apps/openmw/mwgui/tradeitemmodel.hpp index d395744d2a..856f33563d 100644 --- a/apps/openmw/mwgui/tradeitemmodel.hpp +++ b/apps/openmw/mwgui/tradeitemmodel.hpp @@ -31,6 +31,9 @@ namespace MWGui void returnItemBorrowedFromUs(ModelIndex itemIndex, ItemModel* source, size_t count); + /// Update borrowed items in this model + void updateBorrowed(); + /// Permanently transfers items that were borrowed to us from another model to this model void transferItems(); /// Aborts trade diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index ad5165c6ae..60ec6d589a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -651,4 +651,13 @@ namespace MWGui { mItemView->update(); } + + void TradeWindow::itemRemoved(const MWWorld::ConstPtr& item, int count) + { + mTradeModel->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->updateBorrowed(); + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + updateItemView(); + } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 8614655c47..c4bde5fc11 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -44,10 +44,12 @@ namespace MWGui void onDeleteCustomData(const MWWorld::Ptr& ptr) override; + TradeItemModel* getTradeModel() { return mTradeModel; }; + void updateItemView(); void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } - void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override; typedef MyGUI::delegates::MultiDelegate<> EventHandle_TradeDone; EventHandle_TradeDone eventTradeDone; From 0247082e198ded78e06b13d6c039d102d7de43e0 Mon Sep 17 00:00:00 2001 From: Kindi Date: Thu, 24 Apr 2025 20:24:55 +0800 Subject: [PATCH 07/32] fix signedness, remove extra semicolon, update offer --- apps/openmw/mwgui/tradeitemmodel.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 2 ++ apps/openmw/mwgui/tradewindow.hpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index 5df048d6db..660e940367 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -118,7 +118,7 @@ namespace MWGui auto update = [](std::vector& list) { for (auto it = list.begin(); it != list.end();) { - int actualCount = it->mBase.getCellRef().getCount(); + size_t actualCount = it->mBase.getCellRef().getCount(); if (actualCount < it->mCount) it->mCount = actualCount; if (it->mCount == 0) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 60ec6d589a..cf882d96cf 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -659,5 +659,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); updateItemView(); + + updateOffer(); } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index c4bde5fc11..748d7ce7d5 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -44,7 +44,7 @@ namespace MWGui void onDeleteCustomData(const MWWorld::Ptr& ptr) override; - TradeItemModel* getTradeModel() { return mTradeModel; }; + TradeItemModel* getTradeModel() { return mTradeModel; } void updateItemView(); From 2df7ded814e3fd3f8e997ad29ab609cf7c8df23f Mon Sep 17 00:00:00 2001 From: Kuyondo Date: Fri, 23 May 2025 18:15:24 +0800 Subject: [PATCH 08/32] respawning npc respawns in origin cell --- apps/openmw/mwclass/npc.cpp | 3 ++- apps/openmw/mwworld/cellstore.cpp | 9 +++++++++ apps/openmw/mwworld/cellstore.hpp | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0b61436d11..c03d132503 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1427,7 +1427,8 @@ namespace MWClass ptr.getRefData().setCustomData(nullptr); // Reset to original position - MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3()); + MWBase::Environment::get().getWorld()->moveObject( + ptr, ptr.getCell()->getOriginCell(ptr), ptr.getCellRef().getPosition().asVec3()); MWBase::Environment::get().getWorld()->rotateObject( ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none); } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index fca0135e13..61618c001e 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -1354,6 +1354,15 @@ namespace MWWorld return {}; } + CellStore* MWWorld::CellStore::getOriginCell(const Ptr& object) const + { + MovedRefTracker::const_iterator found = mMovedHere.find(object.getBase()); + if (found != mMovedHere.end()) + return found->second; + + return object.getCell(); + } + Ptr CellStore::getPtr(ESM::RefId id) { if (mState == CellStore::State_Unloaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 126935ace5..59127d6186 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -338,6 +338,8 @@ namespace MWWorld Ptr getMovedActor(int actorId) const; + CellStore* getOriginCell(const Ptr& object) const; + Ptr getPtr(ESM::RefId id); private: From 86783e87580fe6e73a0852ed71d76ff34ade824e Mon Sep 17 00:00:00 2001 From: Kuyondo Date: Fri, 23 May 2025 23:46:42 +0800 Subject: [PATCH 09/32] also for creatures --- apps/openmw/mwclass/creature.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 9224f6f0d8..324ba5f98b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -871,7 +871,8 @@ namespace MWClass ptr.getRefData().setCustomData(nullptr); // Reset to original position - MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3()); + MWBase::Environment::get().getWorld()->moveObject( + ptr, ptr.getCell()->getOriginCell(ptr), ptr.getCellRef().getPosition().asVec3()); MWBase::Environment::get().getWorld()->rotateObject( ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none); } From 206d38f3d7b71ecd78f31d627e52d1d4ca9de8c1 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 2 Jul 2025 16:58:31 +0200 Subject: [PATCH 10/32] Fix path handling for files in BSAs --- apps/bulletobjecttool/main.cpp | 2 +- apps/components_tests/fx/technique.cpp | 3 +- apps/navmeshtool/main.cpp | 2 +- apps/niftest/niftest.cpp | 4 +- apps/opencs/model/world/data.cpp | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwgui/postprocessorhud.cpp | 38 ++++++++------ apps/openmw/mwgui/postprocessorhud.hpp | 3 +- apps/openmw/mwrender/postprocessor.cpp | 40 ++++++++------ apps/openmw/mwrender/postprocessor.hpp | 6 +-- components/bsa/ba2dx10file.hpp | 1 + components/bsa/ba2gnrlfile.hpp | 1 + components/bsa/bsafile.hpp | 16 +++--- components/bsa/compressedbsafile.hpp | 1 + components/fx/technique.cpp | 22 ++++---- components/fx/technique.hpp | 10 ++-- components/testing/util.hpp | 4 +- components/vfs/bsaarchive.hpp | 72 ++++++++++++++++++++------ components/vfs/file.hpp | 5 +- components/vfs/filesystemarchive.cpp | 10 ++++ components/vfs/filesystemarchive.hpp | 4 +- components/vfs/manager.cpp | 19 ++++--- components/vfs/manager.hpp | 7 ++- components/vfs/pathutil.hpp | 14 +++++ components/vfs/registerarchives.cpp | 4 +- components/vfs/registerarchives.hpp | 7 ++- 26 files changed, 201 insertions(+), 100 deletions(-) diff --git a/apps/bulletobjecttool/main.cpp b/apps/bulletobjecttool/main.cpp index 5cacedd07e..659d97b7f1 100644 --- a/apps/bulletobjecttool/main.cpp +++ b/apps/bulletobjecttool/main.cpp @@ -155,7 +155,7 @@ namespace VFS::Manager vfs; - VFS::registerArchives(&vfs, fileCollections, archives, true); + VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder()); Settings::Manager::load(config); diff --git a/apps/components_tests/fx/technique.cpp b/apps/components_tests/fx/technique.cpp index ad57074b18..2becf4da5b 100644 --- a/apps/components_tests/fx/technique.cpp +++ b/apps/components_tests/fx/technique.cpp @@ -113,7 +113,8 @@ namespace void compile(const std::string& name) { - mTechnique = std::make_unique(*mVFS.get(), mImageManager, name, 1, 1, true, true); + mTechnique = std::make_unique( + *mVFS.get(), mImageManager, Technique::makeFileName(name), name, 1, 1, true, true); mTechnique->compile(); } }; diff --git a/apps/navmeshtool/main.cpp b/apps/navmeshtool/main.cpp index 08ed10c3b3..6cfa7fc61d 100644 --- a/apps/navmeshtool/main.cpp +++ b/apps/navmeshtool/main.cpp @@ -189,7 +189,7 @@ namespace NavMeshTool VFS::Manager vfs; - VFS::registerArchives(&vfs, fileCollections, archives, true); + VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder()); Settings::Manager::load(config); diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 8634134665..7f19008bd1 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -113,7 +113,7 @@ bool isBSA(const std::filesystem::path& path) std::unique_ptr makeArchive(const std::filesystem::path& path) { if (isBSA(path)) - return VFS::makeBsaArchive(path); + return VFS::makeBsaArchive(path, nullptr); if (std::filesystem::is_directory(path)) return std::make_unique(path); return nullptr; @@ -198,7 +198,7 @@ void readVFS(std::unique_ptr&& archive, const std::filesystem::pat { try { - readVFS(VFS::makeBsaArchive(file.second), file.second, quiet); + readVFS(VFS::makeBsaArchive(file.second, nullptr), file.second, quiet); } catch (const std::exception& e) { diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 00e5fec7b0..fa9251b949 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -143,7 +143,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data , mArchives(archives) , mVFS(std::make_unique()) { - VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true); + VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder()); mResourcesManager.setVFS(mVFS.get()); @@ -1465,7 +1465,7 @@ std::vector CSMWorld::Data::getIds(bool listDeleted) const void CSMWorld::Data::assetsChanged() { mVFS.get()->reset(); - VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true); + VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder()); const UniversalId assetTableIds[] = { UniversalId::Type_Meshes, UniversalId::Type_Icons, UniversalId::Type_Musics, UniversalId::Type_SoundsRes, UniversalId::Type_Textures, UniversalId::Type_Videos }; diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 0f82e953c1..29cfb41071 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -729,7 +729,7 @@ void OMW::Engine::prepareEngine() mVFS = std::make_unique(); - VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true); + VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true, &mEncoder.get()->getStatelessEncoder()); mResourceSystem = std::make_unique( mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder()); diff --git a/apps/openmw/mwgui/postprocessorhud.cpp b/apps/openmw/mwgui/postprocessorhud.cpp index 7712594c54..cc95203a0f 100644 --- a/apps/openmw/mwgui/postprocessorhud.cpp +++ b/apps/openmw/mwgui/postprocessorhud.cpp @@ -33,6 +33,14 @@ namespace MWGui { + namespace + { + std::shared_ptr& getTechnique(const MyGUI::ListBox& list, size_t selected) + { + return *list.getItemDataAt>(selected); + } + } + void PostProcessorHud::ListWrapper::onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char ch) { if (MyGUI::InputManager::getInstance().isShiftPressed() @@ -117,7 +125,7 @@ namespace MWGui if (index >= sender->getItemCount()) return; - updateConfigView(sender->getItemNameAt(index)); + updateConfigView(getTechnique(*sender, index)->getFileName()); } void PostProcessorHud::toggleTechnique(bool enabled) @@ -131,7 +139,7 @@ namespace MWGui auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor(); mOverrideHint = list->getItemNameAt(selected); - auto technique = *list->getItemDataAt>(selected); + auto technique = getTechnique(*list, selected); if (technique->getDynamic()) return; @@ -167,7 +175,7 @@ namespace MWGui if (static_cast(index) != selected) { - auto technique = *mActiveList->getItemDataAt>(selected); + auto technique = getTechnique(*mActiveList, selected); if (technique->getDynamic() || technique->getInternal()) return; @@ -290,16 +298,16 @@ namespace MWGui return; if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE) - updateConfigView(mInactiveList->getItemNameAt(mInactiveList->getIndexSelected())); + updateConfigView(getTechnique(*mInactiveList, mInactiveList->getIndexSelected())->getFileName()); else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE) - updateConfigView(mActiveList->getItemNameAt(mActiveList->getIndexSelected())); + updateConfigView(getTechnique(*mActiveList, mActiveList->getIndexSelected())->getFileName()); } - void PostProcessorHud::updateConfigView(const std::string& name) + void PostProcessorHud::updateConfigView(VFS::Path::NormalizedView path) { auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor(); - auto technique = processor->loadTechnique(name); + auto technique = processor->loadTechnique(path); if (technique->getStatus() == fx::Technique::Status::File_Not_exists) return; @@ -423,22 +431,22 @@ namespace MWGui auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor(); - std::vector techniques; - for (const auto& [name, _] : processor->getTechniqueMap()) - techniques.push_back(name); - std::sort(techniques.begin(), techniques.end(), Misc::StringUtils::ciLess); + std::vector techniques; + for (const auto& vfsPath : processor->getTechniqueFiles()) + techniques.emplace_back(vfsPath); + std::sort(techniques.begin(), techniques.end()); - for (const std::string& name : techniques) + for (VFS::Path::NormalizedView path : techniques) { - auto technique = processor->loadTechnique(name); + auto technique = processor->loadTechnique(path); if (!technique->getHidden() && !processor->isTechniqueEnabled(technique)) { - std::string lowerName = Utf8Stream::lowerCaseUtf8(name); + std::string lowerName = Utf8Stream::lowerCaseUtf8(technique->getName()); std::string lowerCaption = mFilter->getCaption(); lowerCaption = Utf8Stream::lowerCaseUtf8(lowerCaption); if (lowerName.find(lowerCaption) != std::string::npos) - mInactiveList->addItem(name, technique); + mInactiveList->addItem(technique->getName(), technique); } } diff --git a/apps/openmw/mwgui/postprocessorhud.hpp b/apps/openmw/mwgui/postprocessorhud.hpp index 20e27bac3a..0028999966 100644 --- a/apps/openmw/mwgui/postprocessorhud.hpp +++ b/apps/openmw/mwgui/postprocessorhud.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace MyGUI { @@ -48,7 +49,7 @@ namespace MWGui void notifyFilterChanged(MyGUI::EditBox* sender); - void updateConfigView(const std::string& name); + void updateConfigView(VFS::Path::NormalizedView path); void notifyResetButtonClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 1f1b7258b3..3a2d5218d5 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -250,14 +251,12 @@ namespace MWRender void PostProcessor::populateTechniqueFiles() { - for (const auto& name : mVFS->getRecursiveDirectoryIterator(fx::Technique::sSubdir)) + for (const auto& path : mVFS->getRecursiveDirectoryIterator(fx::Technique::sSubdir)) { - std::filesystem::path path = Files::pathFromUnicodeString(name); - std::string fileExt = Misc::StringUtils::lowerCase(Files::pathToUnicodeString(path.extension())); - if (!path.parent_path().has_parent_path() && fileExt == fx::Technique::sExt) + std::string_view fileExt = Misc::getFileExtension(path); + if (path.parent().parent().empty() && fileExt == fx::Technique::sExt) { - const auto absolutePath = mVFS->getAbsoluteFileName(path); - mTechniqueFileMap[Files::pathToUnicodeString(absolutePath.stem())] = absolutePath; + mTechniqueFiles.emplace(path); } } } @@ -351,7 +350,7 @@ namespace MWRender if (technique->getStatus() == fx::Technique::Status::File_Not_exists) continue; - const auto lastWriteTime = std::filesystem::last_write_time(mTechniqueFileMap[technique->getName()]); + const auto lastWriteTime = mVFS->getLastModified(technique->getFileName()); const bool isDirty = technique->setLastModificationTime(lastWriteTime); if (!isDirty) @@ -363,7 +362,7 @@ namespace MWRender std::this_thread::sleep_for(std::chrono::milliseconds(5)); if (technique->compile()) - Log(Debug::Info) << "Reloaded technique : " << mTechniqueFileMap[technique->getName()]; + Log(Debug::Info) << "Reloaded technique : " << technique->getFileName(); mReload = technique->isValid(); } @@ -750,27 +749,31 @@ namespace MWRender } std::shared_ptr PostProcessor::loadTechnique(const std::string& name, bool loadNextFrame) + { + VFS::Path::Normalized path = fx::Technique::makeFileName(name); + return loadTechnique(VFS::Path::NormalizedView(path), loadNextFrame); + } + + std::shared_ptr PostProcessor::loadTechnique(VFS::Path::NormalizedView path, bool loadNextFrame) { for (const auto& technique : mTemplates) - if (Misc::StringUtils::ciEqual(technique->getName(), name)) + if (technique->getFileName() == path) return technique; for (const auto& technique : mQueuedTemplates) - if (Misc::StringUtils::ciEqual(technique->getName(), name)) + if (technique->getFileName() == path) return technique; - std::string realName = name; - auto fileIter = mTechniqueFileMap.find(name); - if (fileIter != mTechniqueFileMap.end()) - realName = fileIter->first; + if (!mTechniqueFiles.contains(path)) + return {}; auto technique = std::make_shared(*mVFS, *mRendering.getResourceSystem()->getImageManager(), - std::move(realName), renderWidth(), renderHeight(), mUBO, mNormalsSupported); + path, mVFS->getStem(path), renderWidth(), renderHeight(), mUBO, mNormalsSupported); technique->compile(); if (technique->getStatus() != fx::Technique::Status::File_Not_exists) - technique->setLastModificationTime(std::filesystem::last_write_time(fileIter->second)); + technique->setLastModificationTime(mVFS->getLastModified(path)); if (loadNextFrame) { @@ -802,7 +805,10 @@ namespace MWRender if (techniqueName.empty()) continue; - mTechniques.push_back(loadTechnique(techniqueName)); + auto technique = loadTechnique(techniqueName); + if (!technique) + continue; + mTechniques.push_back(std::move(technique)); } dirtyTechniques(); diff --git a/apps/openmw/mwrender/postprocessor.hpp b/apps/openmw/mwrender/postprocessor.hpp index 6b1f4612f1..4b854cbc9c 100644 --- a/apps/openmw/mwrender/postprocessor.hpp +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -128,7 +128,7 @@ namespace MWRender const TechniqueList& getTemplates() const { return mTemplates; } - const auto& getTechniqueMap() const { return mTechniqueFileMap; } + const auto& getTechniqueFiles() const { return mTechniqueFiles; } void resize(); @@ -176,6 +176,7 @@ namespace MWRender void toggleMode(); + std::shared_ptr loadTechnique(VFS::Path::NormalizedView path, bool loadNextFrame = false); std::shared_ptr loadTechnique(const std::string& name, bool loadNextFrame = false); TechniqueList getChain(); @@ -232,8 +233,7 @@ namespace MWRender TechniqueList mQueuedTemplates; TechniqueList mInternalTechniques; - std::unordered_map - mTechniqueFileMap; + std::unordered_set> mTechniqueFiles; RenderingManager& mRendering; osgViewer::Viewer* mViewer; diff --git a/components/bsa/ba2dx10file.hpp b/components/bsa/ba2dx10file.hpp index cd1f822179..c902a4ccb0 100644 --- a/components/bsa/ba2dx10file.hpp +++ b/components/bsa/ba2dx10file.hpp @@ -50,6 +50,7 @@ namespace Bsa public: using BSAFile::getFilename; using BSAFile::getList; + using BSAFile::getPath; using BSAFile::open; BA2DX10File(); diff --git a/components/bsa/ba2gnrlfile.hpp b/components/bsa/ba2gnrlfile.hpp index 0bc94eae0e..080ba3a8df 100644 --- a/components/bsa/ba2gnrlfile.hpp +++ b/components/bsa/ba2gnrlfile.hpp @@ -38,6 +38,7 @@ namespace Bsa public: using BSAFile::getFilename; using BSAFile::getList; + using BSAFile::getPath; using BSAFile::open; BA2GNRLFile(); diff --git a/components/bsa/bsafile.hpp b/components/bsa/bsafile.hpp index ad7acdad17..7b910208d8 100644 --- a/components/bsa/bsafile.hpp +++ b/components/bsa/bsafile.hpp @@ -84,15 +84,15 @@ namespace Bsa protected: bool mHasChanged = false; + /// True when an archive has been loaded + bool mIsLoaded = false; + /// Table of files in this archive FileList mFiles; /// Filename string buffer std::vector mStringBuf; - /// True when an archive has been loaded - bool mIsLoaded; - /// Used for error messages std::filesystem::path mFilepath; @@ -109,11 +109,6 @@ namespace Bsa * ----------------------------------- */ - BSAFile() - : mIsLoaded(false) - { - } - virtual ~BSAFile() { close(); @@ -148,6 +143,11 @@ namespace Bsa return Files::pathToUnicodeString(mFilepath); } + const std::filesystem::path& getPath() const + { + return mFilepath; + } + // checks version of BSA from file header static BsaVersion detectVersion(const std::filesystem::path& filePath); }; diff --git a/components/bsa/compressedbsafile.hpp b/components/bsa/compressedbsafile.hpp index 83620f11bc..1e359ea3fe 100644 --- a/components/bsa/compressedbsafile.hpp +++ b/components/bsa/compressedbsafile.hpp @@ -117,6 +117,7 @@ namespace Bsa public: using BSAFile::getFilename; using BSAFile::getList; + using BSAFile::getPath; using BSAFile::open; CompressedBSAFile() = default; diff --git a/components/fx/technique.cpp b/components/fx/technique.cpp index a8cd455dea..58ea55b2f6 100644 --- a/components/fx/technique.cpp +++ b/components/fx/technique.cpp @@ -37,22 +37,20 @@ namespace namespace fx { - namespace + VFS::Path::Normalized Technique::makeFileName(std::string_view name) { - VFS::Path::Normalized makeFilePath(std::string_view name) - { - std::string fileName(name); - fileName += Technique::sExt; - VFS::Path::Normalized result(Technique::sSubdir); - result /= fileName; - return result; - } + std::string fileName(name); + fileName += '.'; + fileName += Technique::sExt; + VFS::Path::Normalized result(Technique::sSubdir); + result /= fileName; + return result; } - Technique::Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, std::string name, int width, - int height, bool ubo, bool supportsNormals) + Technique::Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, + VFS::Path::NormalizedView fileName, std::string name, int width, int height, bool ubo, bool supportsNormals) : mName(std::move(name)) - , mFilePath(makeFilePath(mName)) + , mFilePath(fileName) , mLastModificationTime(std::filesystem::file_time_type::clock::now()) , mWidth(width) , mHeight(height) diff --git a/components/fx/technique.hpp b/components/fx/technique.hpp index ad5e876faa..00b87a86e2 100644 --- a/components/fx/technique.hpp +++ b/components/fx/technique.hpp @@ -105,8 +105,8 @@ namespace fx using UniformMap = std::vector>; using RenderTargetMap = std::unordered_map; - static constexpr std::string_view sExt = ".omwfx"; - static constexpr std::string_view sSubdir = "shaders"; + static constexpr std::string_view sExt = "omwfx"; + static constexpr VFS::Path::NormalizedView sSubdir{ "shaders" }; enum class Status { @@ -123,8 +123,10 @@ namespace fx static constexpr FlagsType Flag_Disable_SunGlare = (1 << 4); static constexpr FlagsType Flag_Hidden = (1 << 5); - Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, std::string name, int width, - int height, bool ubo, bool supportsNormals); + static VFS::Path::Normalized makeFileName(std::string_view name); + + Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, VFS::Path::NormalizedView fileName, + std::string name, int width, int height, bool ubo, bool supportsNormals); bool compile(); diff --git a/components/testing/util.hpp b/components/testing/util.hpp index 53331d6d37..e2183d5403 100644 --- a/components/testing/util.hpp +++ b/components/testing/util.hpp @@ -56,7 +56,9 @@ namespace TestingOpenMW Files::IStreamPtr open() override { return std::make_unique(mContent, std::ios_base::in); } - std::filesystem::path getPath() override { return "TestFile"; } + std::filesystem::file_time_type getLastModified() const override { return {}; } + + std::string getStem() const override { return "TestFile"; } private: const std::string mContent; diff --git a/components/vfs/bsaarchive.hpp b/components/vfs/bsaarchive.hpp index 2e6fac6558..f89e47d971 100644 --- a/components/vfs/bsaarchive.hpp +++ b/components/vfs/bsaarchive.hpp @@ -10,45 +10,72 @@ #include #include +#include + #include #include #include namespace VFS { + template + class BsaArchive; + template class BsaArchiveFile : public File { public: - BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, FileType* bsa) + BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, const BsaArchive* bsa) : mInfo(info) , mFile(bsa) { } - Files::IStreamPtr open() override { return mFile->getFile(mInfo); } + Files::IStreamPtr open() override { return mFile->getFile()->getFile(mInfo); } - std::filesystem::path getPath() override { return mInfo->name(); } + std::filesystem::file_time_type getLastModified() const override + { + return std::filesystem::last_write_time(mFile->getFile()->getPath()); + } + + std::string getStem() const override + { + std::string_view name = mInfo->name(); + auto index = name.find_last_of("\\/"); + if (index != std::string_view::npos) + name = name.substr(index + 1); + index = name.find_last_of('.'); + if (index != std::string_view::npos && index != 0) + name = name.substr(0, index); + std::string out; + std::string_view utf8 = mFile->getUtf8(name, out); + if (out.data() == utf8.data()) + out.resize(utf8.size()); + else + out = utf8; + return out; + } const Bsa::BSAFile::FileStruct* mInfo; - FileType* mFile; + const BsaArchive* mFile; }; template class BsaArchive : public Archive { public: - BsaArchive(const std::filesystem::path& filename) + BsaArchive(const std::filesystem::path& filename, const ToUTF8::StatelessUtf8Encoder* encoder) : Archive() + , mEncoder(encoder) { mFile = std::make_unique(); mFile->open(filename); - const Bsa::BSAFile::FileList& filelist = mFile->getList(); - for (Bsa::BSAFile::FileList::const_iterator it = filelist.begin(); it != filelist.end(); ++it) + std::string buffer; + for (const Bsa::BSAFile::FileStruct& file : mFile->getList()) { - mResources.emplace_back(&*it, mFile.get()); - mFiles.emplace_back(it->name()); + mResources.emplace_back(&file, this); + mFiles.emplace_back(getUtf8(file.name(), buffer)); } std::sort(mFiles.begin(), mFiles.end()); @@ -56,8 +83,12 @@ namespace VFS void listResources(FileMap& out) override { + std::string buffer; for (auto& resource : mResources) - out[VFS::Path::Normalized(resource.mInfo->name())] = &resource; + { + std::string_view path = getUtf8(resource.mInfo->name(), buffer); + out[VFS::Path::Normalized(path)] = &resource; + } } bool contains(Path::NormalizedView file) const override @@ -67,26 +98,37 @@ namespace VFS std::string getDescription() const override { return std::string{ "BSA: " } + mFile->getFilename(); } + BSAFileType* getFile() const { return mFile.get(); } + + std::string_view getUtf8(std::string_view input, std::string& buffer) const + { + if (mEncoder == nullptr) + return input; + return mEncoder->getUtf8(input, ToUTF8::BufferAllocationPolicy::UseGrowFactor, buffer); + } + private: std::unique_ptr mFile; std::vector> mResources; std::vector mFiles; + const ToUTF8::StatelessUtf8Encoder* mEncoder; }; - inline std::unique_ptr makeBsaArchive(const std::filesystem::path& path) + inline std::unique_ptr makeBsaArchive( + const std::filesystem::path& path, const ToUTF8::StatelessUtf8Encoder* encoder) { switch (Bsa::BSAFile::detectVersion(path)) { case Bsa::BsaVersion::Unknown: break; case Bsa::BsaVersion::Uncompressed: - return std::make_unique>(path); + return std::make_unique>(path, encoder); case Bsa::BsaVersion::Compressed: - return std::make_unique>(path); + return std::make_unique>(path, encoder); case Bsa::BsaVersion::BA2GNRL: - return std::make_unique>(path); + return std::make_unique>(path, encoder); case Bsa::BsaVersion::BA2DX10: - return std::make_unique>(path); + return std::make_unique>(path, encoder); } throw std::runtime_error("Unknown archive type '" + Files::pathToUnicodeString(path) + "'"); diff --git a/components/vfs/file.hpp b/components/vfs/file.hpp index f2dadb1162..7c65e3a1ba 100644 --- a/components/vfs/file.hpp +++ b/components/vfs/file.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_VFS_FILE_H #include +#include #include @@ -14,7 +15,9 @@ namespace VFS virtual Files::IStreamPtr open() = 0; - virtual std::filesystem::path getPath() = 0; + virtual std::filesystem::file_time_type getLastModified() const = 0; + + virtual std::string getStem() const = 0; }; } diff --git a/components/vfs/filesystemarchive.cpp b/components/vfs/filesystemarchive.cpp index 3303c6656c..0b67df45bc 100644 --- a/components/vfs/filesystemarchive.cpp +++ b/components/vfs/filesystemarchive.cpp @@ -81,4 +81,14 @@ namespace VFS return Files::openConstrainedFileStream(mPath); } + std::filesystem::file_time_type FileSystemArchiveFile::getLastModified() const + { + return std::filesystem::last_write_time(mPath); + } + + std::string FileSystemArchiveFile::getStem() const + { + return Files::pathToUnicodeString(mPath.stem()); + } + } diff --git a/components/vfs/filesystemarchive.hpp b/components/vfs/filesystemarchive.hpp index 215c443b58..f6b1a2ec5e 100644 --- a/components/vfs/filesystemarchive.hpp +++ b/components/vfs/filesystemarchive.hpp @@ -17,7 +17,9 @@ namespace VFS Files::IStreamPtr open() override; - std::filesystem::path getPath() override { return mPath; } + std::filesystem::file_time_type getLastModified() const override; + + std::string getStem() const override; private: std::filesystem::path mPath; diff --git a/components/vfs/manager.cpp b/components/vfs/manager.cpp index 12ef378017..ff25150f67 100644 --- a/components/vfs/manager.cpp +++ b/components/vfs/manager.cpp @@ -81,15 +81,20 @@ namespace VFS return {}; } - std::filesystem::path Manager::getAbsoluteFileName(const std::filesystem::path& name) const + std::filesystem::file_time_type Manager::getLastModified(VFS::Path::NormalizedView name) const { - std::string normalized = Files::pathToUnicodeString(name); - Path::normalizeFilenameInPlace(normalized); - - const auto found = mIndex.find(normalized); + const auto found = mIndex.find(name); if (found == mIndex.end()) - throw std::runtime_error("Resource '" + normalized + "' is not found"); - return found->second->getPath(); + throw std::runtime_error("Resource '" + std::string(name.value()) + "' not found"); + return found->second->getLastModified(); + } + + std::string Manager::getStem(VFS::Path::NormalizedView name) const + { + const auto found = mIndex.find(name); + if (found == mIndex.end()) + throw std::runtime_error("Resource '" + std::string(name.value()) + "' not found"); + return found->second->getStem(); } RecursiveDirectoryRange Manager::getRecursiveDirectoryIterator(std::string_view path) const diff --git a/components/vfs/manager.hpp b/components/vfs/manager.hpp index b6a9d796cc..3d10b3f355 100644 --- a/components/vfs/manager.hpp +++ b/components/vfs/manager.hpp @@ -72,10 +72,9 @@ namespace VFS RecursiveDirectoryRange getRecursiveDirectoryIterator() const; - /// Retrieve the absolute path to the file - /// @note Throws an exception if the file can not be found. - /// @note May be called from any thread once the index has been built. - std::filesystem::path getAbsoluteFileName(const std::filesystem::path& name) const; + std::filesystem::file_time_type getLastModified(VFS::Path::NormalizedView name) const; + // Equivalent to std::filesystem::path::stem. The result isn't normalized. + std::string getStem(VFS::Path::NormalizedView name) const; private: std::vector> mArchives; diff --git a/components/vfs/pathutil.hpp b/components/vfs/pathutil.hpp index f5393617d7..b4b4f6e278 100644 --- a/components/vfs/pathutil.hpp +++ b/components/vfs/pathutil.hpp @@ -127,6 +127,15 @@ namespace VFS::Path return stream << value.mValue; } + NormalizedView parent() const + { + NormalizedView p; + const std::size_t pos = mValue.find_last_of(separator); + if (pos != std::string_view::npos) + p.mValue = mValue.substr(0, pos); + return p; + } + private: std::string_view mValue; }; @@ -259,6 +268,11 @@ namespace VFS::Path return stream << value.mValue; } + NormalizedView parent() const + { + return NormalizedView(*this).parent(); + } + private: std::string mValue; }; diff --git a/components/vfs/registerarchives.cpp b/components/vfs/registerarchives.cpp index f017b5f73c..1d03766220 100644 --- a/components/vfs/registerarchives.cpp +++ b/components/vfs/registerarchives.cpp @@ -14,7 +14,7 @@ namespace VFS { void registerArchives(VFS::Manager* vfs, const Files::Collections& collections, - const std::vector& archives, bool useLooseFiles) + const std::vector& archives, bool useLooseFiles, const ToUTF8::StatelessUtf8Encoder* encoder) { const Files::PathContainer& dataDirs = collections.getPaths(); @@ -25,7 +25,7 @@ namespace VFS // Last BSA has the highest priority const auto archivePath = collections.getPath(*archive); Log(Debug::Info) << "Adding BSA archive " << archivePath; - vfs->addArchive(makeBsaArchive(archivePath)); + vfs->addArchive(makeBsaArchive(archivePath, encoder)); } else { diff --git a/components/vfs/registerarchives.hpp b/components/vfs/registerarchives.hpp index dac29f87a3..03247b74d3 100644 --- a/components/vfs/registerarchives.hpp +++ b/components/vfs/registerarchives.hpp @@ -3,13 +3,18 @@ #include +namespace ToUTF8 +{ + class StatelessUtf8Encoder; +} + namespace VFS { class Manager; /// @brief Register BSA and file system archives based on the given OpenMW configuration. void registerArchives(VFS::Manager* vfs, const Files::Collections& collections, - const std::vector& archives, bool useLooseFiles); + const std::vector& archives, bool useLooseFiles, const ToUTF8::StatelessUtf8Encoder* encoder); } #endif From a6cf35982005eea1cdc1c6b2c0bac364cedda95a Mon Sep 17 00:00:00 2001 From: Kuyondo <2538602-Kuyondo@users.noreply.gitlab.com> Date: Mon, 28 Apr 2025 17:02:17 +0800 Subject: [PATCH 11/32] update offer player side, next frame update view --- apps/openmw/mwgui/companionwindow.cpp | 17 +++++++++-- apps/openmw/mwgui/companionwindow.hpp | 7 ++--- apps/openmw/mwgui/container.cpp | 21 +++++++++++-- apps/openmw/mwgui/container.hpp | 9 +++--- apps/openmw/mwgui/draganddrop.cpp | 13 +++++--- apps/openmw/mwgui/inventorywindow.cpp | 44 ++++++++++++--------------- apps/openmw/mwgui/inventorywindow.hpp | 4 +-- apps/openmw/mwgui/tradewindow.cpp | 24 ++++++++++----- apps/openmw/mwgui/tradewindow.hpp | 18 ++++++----- 9 files changed, 96 insertions(+), 61 deletions(-) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 731403f3a3..bdcb8abc9b 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -43,6 +43,7 @@ namespace MWGui , mSortModel(nullptr) , mModel(nullptr) , mSelectedItem(-1) + , mUpdateNextFrame(false) , mDragAndDrop(dragAndDrop) , mMessageBoxManager(manager) { @@ -141,7 +142,13 @@ namespace MWGui void CompanionWindow::onFrame(float dt) { checkReferenceAvailable(); - updateEncumbranceBar(); + + if (mUpdateNextFrame) + { + updateEncumbranceBar(); + mItemView->update(); + mUpdateNextFrame = false; + } } void CompanionWindow::updateEncumbranceBar() @@ -204,9 +211,13 @@ namespace MWGui mSortModel = nullptr; } - void CompanionWindow::updateItemView() + void CompanionWindow::itemAdded(const MWWorld::ConstPtr& item, int count) { - mItemView->update(); + mUpdateNextFrame = true; } + void CompanionWindow::itemRemoved(const MWWorld::ConstPtr& item, int count) + { + mUpdateNextFrame = true; + } } diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 6f8452997f..5a4b5880ff 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -32,10 +32,8 @@ namespace MWGui void onFrame(float dt) override; void clear() override { resetReference(); } - void updateItemView(); - - void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } - void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemAdded(const MWWorld::ConstPtr& item, int count) override; + void itemRemoved(const MWWorld::ConstPtr& item, int count) override; std::string_view getWindowIdForLua() const override { return "Companion"; } @@ -44,6 +42,7 @@ namespace MWGui SortFilterItemModel* mSortModel; CompanionItemModel* mModel; int mSelectedItem; + bool mUpdateNextFrame; DragAndDrop* mDragAndDrop; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index bb0f90ddc9..6e768f6e1e 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -38,6 +38,7 @@ namespace MWGui , mSortModel(nullptr) , mModel(nullptr) , mSelectedItem(-1) + , mUpdateNextFrame(false) , mTreatNextOpenAsLoot(false) { getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); @@ -323,8 +324,24 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } - void ContainerWindow::updateItemView() + void ContainerWindow::onFrame(float dt) { - mItemView->update(); + checkReferenceAvailable(); + + if (mUpdateNextFrame) + { + mItemView->update(); + mUpdateNextFrame = false; + } + } + + void ContainerWindow::itemAdded(const MWWorld::ConstPtr& item, int count) + { + mUpdateNextFrame = true; + } + + void ContainerWindow::itemRemoved(const MWWorld::ConstPtr& item, int count) + { + mUpdateNextFrame = true; } } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 4898a0eb1c..a7702ebbfe 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -32,7 +32,7 @@ namespace MWGui void onClose() override; void clear() override { resetReference(); } - void onFrame(float dt) override { checkReferenceAvailable(); } + void onFrame(float dt) override; void resetReference() override; @@ -40,10 +40,8 @@ namespace MWGui void treatNextOpenAsLoot() { mTreatNextOpenAsLoot = true; } - void updateItemView(); - - void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } - void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemAdded(const MWWorld::ConstPtr& item, int count) override; + void itemRemoved(const MWWorld::ConstPtr& item, int count) override; std::string_view getWindowIdForLua() const override { return "Container"; } @@ -54,6 +52,7 @@ namespace MWGui SortFilterItemModel* mSortModel; ItemModel* mModel; int mSelectedItem; + bool mUpdateNextFrame; bool mTreatNextOpenAsLoot; MyGUI::Button* mDisposeCorpseButton; MyGUI::Button* mTakeButton; diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index 17ce5360c0..52b0939238 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -84,13 +84,12 @@ namespace MWGui mDraggedWidget->setNeedMouseFocus(false); mDraggedWidget->setCount(count); - sourceView->update(); - MWBase::Environment::get().getWindowManager()->setDragDrop(true); mIsOnDragAndDrop = true; - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + // Update item view after completing drag-and-drop setup + mSourceView->update(); } void DragAndDrop::drop(ItemModel* targetModel, ItemView* targetView) @@ -153,8 +152,12 @@ namespace MWGui // since mSourceView doesn't get updated in drag() MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); - MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); - mDraggedWidget = nullptr; + if (mDraggedWidget) + { + MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); + mDraggedWidget = nullptr; + } + MWBase::Environment::get().getWindowManager()->setDragDrop(false); } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 656f1a3db4..43163e175d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -86,7 +86,7 @@ namespace MWGui , mLastYSize(0) , mPreview(std::make_unique(parent, resourceSystem, MWMechanics::getPlayer())) , mTrading(false) - , mUpdateTimer(0.f) + , mUpdateNextFrame(false) { mPreviewTexture = std::make_unique(mPreview->getTexture(), mPreview->getTextureStateSet()); @@ -683,22 +683,21 @@ namespace MWGui void InventoryWindow::onFrame(float dt) { - updateEncumbranceBar(); - - if (mPinned) + if (mUpdateNextFrame) { - mUpdateTimer += dt; - if (0.1f < mUpdateTimer) + if (mTrading) { - mUpdateTimer = 0; - - // Update pinned inventory in-game - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - mItemView->update(); - notifyContentChanged(); - } + mTradeModel->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->mTradeModel->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->updateItemView(); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->updateOffer(); } + + updateEncumbranceBar(); + mDragAndDrop->update(); + mItemView->update(); + notifyContentChanged(); + mUpdateNextFrame = false; } } @@ -850,19 +849,14 @@ namespace MWGui mPreview->rebuild(); } + void InventoryWindow::itemAdded(const MWWorld::ConstPtr& item, int count) + { + mUpdateNextFrame = true; + } + void InventoryWindow::itemRemoved(const MWWorld::ConstPtr& item, int count) { - if (mDragAndDrop->mIsOnDragAndDrop && mDragAndDrop->mItem.mBase == item) - mDragAndDrop->update(); - - if (mTrading) - { - mTradeModel->updateBorrowed(); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->getTradeModel()->updateBorrowed(); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->updateItemView(); - } - - updateItemView(); + mUpdateNextFrame = true; } MyGUI::IntSize InventoryWindow::getPreviewViewportSize() const diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 079e629673..7f0d64dce0 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -63,7 +63,7 @@ namespace MWGui void setGuiMode(GuiMode mode); - void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemAdded(const MWWorld::ConstPtr& item, int count) override; void itemRemoved(const MWWorld::ConstPtr& item, int count) override; /// Cycle to previous/next weapon @@ -111,7 +111,7 @@ namespace MWGui std::unique_ptr mPreview; bool mTrading; - float mUpdateTimer; + bool mUpdateNextFrame; void toggleMaximized(); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index cf882d96cf..ea124f0a97 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -123,6 +123,7 @@ namespace MWGui , mItemToSell(-1) , mCurrentBalance(0) , mCurrentMerchantOffer(0) + , mUpdateNextFrame(false) { getWidget(mFilterAll, "AllButton"); getWidget(mFilterWeapon, "WeaponButton"); @@ -209,6 +210,16 @@ namespace MWGui void TradeWindow::onFrame(float dt) { checkReferenceAvailable(); + + if (isVisible() && mUpdateNextFrame) + { + mTradeModel->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + mItemView->update(); + updateOffer(); + mUpdateNextFrame = false; + } } void TradeWindow::onNameFilterChanged(MyGUI::EditBox* _sender) @@ -652,14 +663,13 @@ namespace MWGui mItemView->update(); } + void TradeWindow::itemAdded(const MWWorld::ConstPtr& item, int count) + { + mUpdateNextFrame = true; + } + void TradeWindow::itemRemoved(const MWWorld::ConstPtr& item, int count) { - mTradeModel->updateBorrowed(); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->updateBorrowed(); - - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); - updateItemView(); - - updateOffer(); + mUpdateNextFrame = true; } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 748d7ce7d5..b0581d0d38 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -33,22 +33,15 @@ namespace MWGui void onFrame(float dt) override; void clear() override { resetReference(); } - void borrowItem(int index, size_t count); - void returnItem(int index, size_t count); - - int getMerchantServices(); - bool exit() override; void resetReference() override; void onDeleteCustomData(const MWWorld::Ptr& ptr) override; - TradeItemModel* getTradeModel() { return mTradeModel; } - void updateItemView(); - void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemAdded(const MWWorld::ConstPtr& item, int count) override; void itemRemoved(const MWWorld::ConstPtr& item, int count) override; typedef MyGUI::delegates::MultiDelegate<> EventHandle_TradeDone; @@ -57,6 +50,8 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Trade"; } private: + friend class InventoryWindow; + ItemView* mItemView; SortFilterItemModel* mSortModel; TradeItemModel* mTradeModel; @@ -90,6 +85,8 @@ namespace MWGui int mCurrentBalance; int mCurrentMerchantOffer; + bool mUpdateNextFrame; + void sellToNpc( const MWWorld::Ptr& item, int count, bool boughtItem); ///< only used for adjusting the gold balance void buyFromNpc( @@ -100,6 +97,11 @@ namespace MWGui void onItemSelected(int index); void sellItem(MyGUI::Widget* sender, int count); + void borrowItem(int index, size_t count); + void returnItem(int index, size_t count); + + int getMerchantServices(); + void onFilterChanged(MyGUI::Widget* _sender); void onNameFilterChanged(MyGUI::EditBox* _sender); void onOfferButtonClicked(MyGUI::Widget* _sender); From dbc732231f5eb19377f2bc7a43a99b0543b295fe Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 3 Jul 2025 17:21:50 +0200 Subject: [PATCH 12/32] Allow non-existent techniques to exist --- apps/openmw/mwrender/postprocessor.cpp | 20 ++++++++++++-------- apps/openmw/mwrender/postprocessor.hpp | 2 +- components/vfs/pathutil.hpp | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 3a2d5218d5..379b71bc66 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -748,7 +748,7 @@ namespace MWRender return technique->isValid(); } - std::shared_ptr PostProcessor::loadTechnique(const std::string& name, bool loadNextFrame) + std::shared_ptr PostProcessor::loadTechnique(std::string_view name, bool loadNextFrame) { VFS::Path::Normalized path = fx::Technique::makeFileName(name); return loadTechnique(VFS::Path::NormalizedView(path), loadNextFrame); @@ -764,11 +764,14 @@ namespace MWRender if (technique->getFileName() == path) return technique; - if (!mTechniqueFiles.contains(path)) - return {}; + std::string name; + if (mTechniqueFiles.contains(path)) + name = mVFS->getStem(path); + else + name = path.stem(); auto technique = std::make_shared(*mVFS, *mRendering.getResourceSystem()->getImageManager(), - path, mVFS->getStem(path), renderWidth(), renderHeight(), mUBO, mNormalsSupported); + path, std::move(name), renderWidth(), renderHeight(), mUBO, mNormalsSupported); technique->compile(); @@ -805,10 +808,7 @@ namespace MWRender if (techniqueName.empty()) continue; - auto technique = loadTechnique(techniqueName); - if (!technique) - continue; - mTechniques.push_back(std::move(technique)); + mTechniques.push_back(loadTechnique(techniqueName)); } dirtyTechniques(); @@ -831,7 +831,11 @@ namespace MWRender void PostProcessor::toggleMode() { for (auto& technique : mTemplates) + { + if (technique->getStatus() == fx::Technique::Status::File_Not_exists) + continue; technique->compile(); + } dirtyTechniques(true); } diff --git a/apps/openmw/mwrender/postprocessor.hpp b/apps/openmw/mwrender/postprocessor.hpp index 4b854cbc9c..eb9bc6347c 100644 --- a/apps/openmw/mwrender/postprocessor.hpp +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -177,7 +177,7 @@ namespace MWRender void toggleMode(); std::shared_ptr loadTechnique(VFS::Path::NormalizedView path, bool loadNextFrame = false); - std::shared_ptr loadTechnique(const std::string& name, bool loadNextFrame = false); + std::shared_ptr loadTechnique(std::string_view name, bool loadNextFrame = false); TechniqueList getChain(); diff --git a/components/vfs/pathutil.hpp b/components/vfs/pathutil.hpp index b4b4f6e278..a890be8a54 100644 --- a/components/vfs/pathutil.hpp +++ b/components/vfs/pathutil.hpp @@ -136,6 +136,18 @@ namespace VFS::Path return p; } + std::string_view stem() const + { + std::string_view stem = mValue; + std::size_t pos = stem.find_last_of(separator); + if (pos != std::string_view::npos) + stem = stem.substr(pos + 1); + pos = stem.find_first_of(extensionSeparator); + if (pos != std::string_view::npos) + stem = stem.substr(0, pos); + return stem; + } + private: std::string_view mValue; }; @@ -273,6 +285,11 @@ namespace VFS::Path return NormalizedView(*this).parent(); } + std::string_view stem() const + { + return NormalizedView(*this).stem(); + } + private: std::string mValue; }; From 7f34e52a11b5da4942e0b30a5b4b90552e24d9be Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Tue, 18 Feb 2025 19:33:29 +0100 Subject: [PATCH 13/32] lua interface for window visibility --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwgui/windowmanagerimp.hpp | 1 + apps/openmw/mwlua/uibindings.cpp | 2 ++ files/data/scripts/omw/ui.lua | 7 +++++++ 5 files changed, 16 insertions(+) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 8164501b4b..037f719e6d 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -384,6 +384,7 @@ namespace MWBase // Used in Lua bindings virtual const std::vector& getGuiModeStack() const = 0; virtual void setDisabledByLua(std::string_view windowId, bool disabled) = 0; + virtual bool isWindowVisible(std::string_view windowId) const = 0; virtual std::vector getAllWindowIds() const = 0; virtual std::vector getAllowedWindowIds(MWGui::GuiMode mode) const = 0; }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 565fb43127..2c1741977c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2406,6 +2406,11 @@ namespace MWGui updateVisible(); } + bool WindowManager::isWindowVisible(std::string_view windowId) const + { + return mLuaIdToWindow.at(windowId)->isVisible(); + } + std::vector WindowManager::getAllWindowIds() const { std::vector res; diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 650e2bab78..1a96092b60 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -390,6 +390,7 @@ namespace MWGui // Used in Lua bindings const std::vector& getGuiModeStack() const override { return mGuiModes; } void setDisabledByLua(std::string_view windowId, bool disabled) override; + bool isWindowVisible(std::string_view windowId) const override; std::vector getAllWindowIds() const override; std::vector getAllowedWindowIds(GuiMode mode) const override; diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index bc5581eb74..826338ca7d 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -296,6 +296,8 @@ namespace MWLua luaManager->addAction( [=, window = std::move(window)]() { windowManager->setDisabledByLua(window, disabled); }); }; + api["_isWindowVisible"] + = [windowManager](std::string_view window) { return windowManager->isWindowVisible(window); }; // TODO // api["_showMouseCursor"] = [](bool) {}; diff --git a/files/data/scripts/omw/ui.lua b/files/data/scripts/omw/ui.lua index e29bbe254e..379396bd35 100644 --- a/files/data/scripts/omw/ui.lua +++ b/files/data/scripts/omw/ui.lua @@ -240,6 +240,13 @@ return { -- @return #boolean isHudVisible = function() return ui._isHudVisible() end, + --- + -- Returns if the given window is visible or not + -- @function [parent=#UI] isWindowVisible + -- @param #string windowName + -- @return #boolean + isWindowVisible = ui._isWindowVisible, + -- TODO -- registerHudElement = function(name, showFn, hideFn) end, -- showHudElement = function(name, bool) end, From 593988e82b8b30be57b209f2ca597f8a3cc7f7c4 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Tue, 18 Feb 2025 20:43:03 +0100 Subject: [PATCH 14/32] bump lua revision --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7301496172..123462b8fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 50) set(OPENMW_VERSION_RELEASE 0) -set(OPENMW_LUA_API_REVISION 78) +set(OPENMW_LUA_API_REVISION 79) set(OPENMW_POSTPROCESSING_API_REVISION 2) set(OPENMW_VERSION_COMMITHASH "") From 9fe420e562d3b5ee02bfa8aedd33ca34c0db88e0 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Tue, 18 Feb 2025 20:59:46 +0100 Subject: [PATCH 15/32] improve error report when windowId is invalid. --- apps/openmw/mwgui/windowmanagerimp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2c1741977c..4ff297dd94 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2408,7 +2408,10 @@ namespace MWGui bool WindowManager::isWindowVisible(std::string_view windowId) const { - return mLuaIdToWindow.at(windowId)->isVisible(); + auto it = mLuaIdToWindow.find(windowId); + if (it == mLuaIdToWindow.end()) + throw std::logic_error("Invalid window name: " + std::string(windowId)); + return it->second->isVisible(); } std::vector WindowManager::getAllWindowIds() const From b1f32c8cee871cee60339a20b5c1cc683bd8a858 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Wed, 19 Feb 2025 14:59:05 +0100 Subject: [PATCH 16/32] isWindowVisible must handle replaced windows' visibility --- files/data/scripts/omw/ui.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/files/data/scripts/omw/ui.lua b/files/data/scripts/omw/ui.lua index 379396bd35..9d5133b3d0 100644 --- a/files/data/scripts/omw/ui.lua +++ b/files/data/scripts/omw/ui.lua @@ -155,6 +155,13 @@ local function onUiModeChangedEvent(data) end end +local function isWindowVisible(windowName) + if replacedWindows[windowName] then + return replacedWindows[windowName].visible + end + return ui._isWindowVisible(windowName) +end + return { interfaceName = 'UI', --- @@ -245,7 +252,7 @@ return { -- @function [parent=#UI] isWindowVisible -- @param #string windowName -- @return #boolean - isWindowVisible = ui._isWindowVisible, + isWindowVisible = isWindowVisible, -- TODO -- registerHudElement = function(name, showFn, hideFn) end, From a5b8db70a4ea2d537e1e15672b1bfe28f48b27c7 Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Fri, 4 Jul 2025 06:59:05 -0700 Subject: [PATCH 17/32] docs - improve styling in headers and code blocks --- docs/source/_ext/omw-lexers.py | 19 +++++ docs/source/_static/theme-override.css | 14 ++++ docs/source/conf.py | 3 +- .../installation/install-game-files.rst | 6 +- .../reference/lua-scripting/overview.rst | 6 +- ...line-blender-collada-animated-creature.rst | 1 - ...pipeline-blender-collada-static-models.rst | 2 +- .../modding/custom-shader-effects.rst | 6 +- docs/source/reference/modding/extended.rst | 21 +++-- docs/source/reference/modding/font.rst | 30 ++++--- .../modding/openmw-game-template.rst | 12 ++- docs/source/reference/modding/paths.rst | 80 +++++++++++++------ .../convert-bump-mapped-mods.rst | 44 +++------- .../texture-modding/texture-basics.rst | 36 +++++---- 14 files changed, 178 insertions(+), 102 deletions(-) create mode 100644 docs/source/_ext/omw-lexers.py diff --git a/docs/source/_ext/omw-lexers.py b/docs/source/_ext/omw-lexers.py new file mode 100644 index 0000000000..02cd526304 --- /dev/null +++ b/docs/source/_ext/omw-lexers.py @@ -0,0 +1,19 @@ +from pygments.lexer import RegexLexer, bygroups +from pygments.token import Comment, Name, Operator, String, Text +from sphinx.highlighting import lexers + +class OMWConfigLexer(RegexLexer): + name = 'openmwcfg' + aliases = ['openmwcfg'] + filenames = ['openmw.cfg'] + + tokens = { + 'root': [ + (r'(\s*)(#.*$)', bygroups(Text.Whitespace, Comment.Single)), + (r'(\s*)([a-zA-Z0-9_.+-]+)(\s*(\+)?=\s*)(.*)', bygroups(Text.Whitespace, Name.Attribute, Operator, Operator, String)), + (r'.+\n', Text), + ], + } + +def setup(_): + lexers["openmwcfg"] = OMWConfigLexer() diff --git a/docs/source/_static/theme-override.css b/docs/source/_static/theme-override.css index e41ab2c2d6..cf9ae168bc 100644 --- a/docs/source/_static/theme-override.css +++ b/docs/source/_static/theme-override.css @@ -138,3 +138,17 @@ tbody tr:hover { #left-sidebar { overflow-y: scroll; } + +#content div[class^=highlight], #content pre.literal-block, p, h4, h5, h6 { + margin-bottom: 1.5rem; +} + +h5 { + font-size: 1.15rem; + font-weight: 600; +} + +h6 { + font-size: 1.08rem; + font-weight: 600; +} \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index aaa42e3678..d6f19c38f1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -43,7 +43,8 @@ extensions = [ 'sphinx.ext.viewcode', 'sphinx.ext.autosectionlabel', 'sphinx_design', - 'omw-directives' + 'omw-directives', + 'omw-lexers', ] #autosectionlabel_prefix_document = True diff --git a/docs/source/manuals/installation/install-game-files.rst b/docs/source/manuals/installation/install-game-files.rst index cb44f7d1de..0dad7bfa4d 100644 --- a/docs/source/manuals/installation/install-game-files.rst +++ b/docs/source/manuals/installation/install-game-files.rst @@ -81,15 +81,15 @@ For Distributions Using `apt` (e.g., Ubuntu, Debian) .. code:: console - sudo apt update - sudo apt install innoextract + $ sudo apt update + $ sudo apt install innoextract For macOS using Homebrew ++++++++++++++++++++++++ .. code:: console - brew install innoextract + $ brew install innoextract Once innoextract is installed, download the game from GOG. The downloaded file should be called ``setup_tes_morrowind_goty_2.0.0.7.exe`` or something similar. When ``innoextract`` is run on it, it will extract the files directly into the folder the ``setup.exe`` file is located. If you have a specific folder where you want it to be extracted to, for example in ``~/Documents/Games/Morrowind`` You can specify it with the ``-d`` flag. diff --git a/docs/source/reference/lua-scripting/overview.rst b/docs/source/reference/lua-scripting/overview.rst index 852d63ca0a..b838385524 100644 --- a/docs/source/reference/lua-scripting/overview.rst +++ b/docs/source/reference/lua-scripting/overview.rst @@ -126,10 +126,12 @@ The options are: Enable it in ``openmw.cfg`` the same way as any other mod: -:: +.. code-block:: openmwcfg + :caption: openmw.cfg data=path/to/my_lua_mod - content=my_lua_mod.omwscripts # or content=my_lua_mod.omwaddon + # or content=my_lua_mod.omwaddon + content=my_lua_mod.omwscripts Now every time the player presses "X" on a keyboard, a message is shown. diff --git a/docs/source/reference/modding/custom-models/pipeline-blender-collada-animated-creature.rst b/docs/source/reference/modding/custom-models/pipeline-blender-collada-animated-creature.rst index f07ba7651a..21da034a54 100644 --- a/docs/source/reference/modding/custom-models/pipeline-blender-collada-animated-creature.rst +++ b/docs/source/reference/modding/custom-models/pipeline-blender-collada-animated-creature.rst @@ -177,7 +177,6 @@ definitions and events. At a minimum it needs to include at least animation runforward: stop 4.433333 attack1: start 4.466667 attack1: stop 5.433333 - ... The textkeys file is placed in the same folder as the model and matches the model's name. diff --git a/docs/source/reference/modding/custom-models/pipeline-blender-collada-static-models.rst b/docs/source/reference/modding/custom-models/pipeline-blender-collada-static-models.rst index d52c952b3d..5fb109c962 100644 --- a/docs/source/reference/modding/custom-models/pipeline-blender-collada-static-models.rst +++ b/docs/source/reference/modding/custom-models/pipeline-blender-collada-static-models.rst @@ -168,7 +168,7 @@ the file path to the texture is incorrect and OpenMW can't find it. To fix this you can open the exported ``.dae`` file in a text editor and check the texture's filepath. In the example of this barrel model it's found on lines 13-17. -.. code:: +.. code-block:: xml diff --git a/docs/source/reference/modding/custom-shader-effects.rst b/docs/source/reference/modding/custom-shader-effects.rst index 0e7776c7ae..41dd4c7369 100644 --- a/docs/source/reference/modding/custom-shader-effects.rst +++ b/docs/source/reference/modding/custom-shader-effects.rst @@ -23,7 +23,8 @@ dungeons. To use this feature the :ref:`soft particles` setting must be enabled. This setting can either be activated in the OpenMW launcher or changed in `settings.cfg`: -:: +.. code-block:: ini + :caption: settings.cfg [Shaders] soft particles = true @@ -64,7 +65,8 @@ Blue and alpha channels are ignored. To use this feature the :ref:`post processing ` setting must be enabled. This setting can either be activated in the OpenMW launcher, in-game, or changed in `settings.cfg`: -:: +.. code-block:: ini + :caption: settings.cfg [Post Processing] enabled = true diff --git a/docs/source/reference/modding/extended.rst b/docs/source/reference/modding/extended.rst index 001492f254..8b5c30fdd0 100644 --- a/docs/source/reference/modding/extended.rst +++ b/docs/source/reference/modding/extended.rst @@ -91,7 +91,8 @@ The behavior of such a model: The actual state toggling time depends on the sunrise/sunset time settings in `openmw.cfg`: -:: +.. code-block:: openmwcfg + :caption: openmw.cfg fallback=Weather_Sunrise_Time,6 fallback=Weather_Sunset_Time,18 @@ -102,7 +103,8 @@ These settings lead to the "night" starting at 20:00 and ending at 6:00. The engine checks if the weather is bright enough to support the "interior day" mode using the Glare_View setting. If it is >= 0.5, the engine considers the weather bright. -:: +.. code-block:: openmwcfg + :caption: openmw.cfg fallback=Weather_Clear_Glare_View,1 fallback=Weather_Foggy_Glare_View,0.25 @@ -138,7 +140,8 @@ If you want to override walking animations, you should override ``xbase_anim_fem To enable this feature, you should have this line in your settings.cfg: -:: +.. code-block:: ini + :caption: settings.cfg [Game] use additional anim sources = true @@ -157,7 +160,8 @@ This feature conflicts with old mods which use scripted scabbards, arrows with p The minimum you need is the ``xbase_anim_sh.nif`` file from the `Weapon Sheathing`_ mod and this line in your settings.cfg: -:: +.. code-block:: ini + :caption: settings.cfg [Game] weapon sheathing = true @@ -205,7 +209,8 @@ Skeleton extensions It is possible to inject custom bones into actor skeletons: -:: +.. code-block:: ini + :caption: settings.cfg [Game] use additional anim sources = true @@ -323,14 +328,16 @@ General advices to create assets for this feature: Groundcover mods can be registered in the openmw.cfg via "groundcover" entries instead of "content" ones: -:: +.. code-block:: openmwcfg + :caption: openmw.cfg groundcover=my_grass_mod.esp Every static from such mod is treated as a groundcover object. Also groundcover detection should be enabled via settings.cfg: -:: +.. code-block:: ini + :caption: settings.cfg [Groundcover] enabled = true diff --git a/docs/source/reference/modding/font.rst b/docs/source/reference/modding/font.rst index 2c67a940d1..b0bb342168 100644 --- a/docs/source/reference/modding/font.rst +++ b/docs/source/reference/modding/font.rst @@ -3,8 +3,11 @@ Fonts Default UI font and font used in magic scrolls are defined in ``openmw.cfg``: - fallback=Fonts_Font_0,MysticCards - fallback=Fonts_Font_2,DemonicLetters +.. code-block:: openmwcfg + :caption: openmw.cfg + + fallback=Fonts_Font_0,MysticCards + fallback=Fonts_Font_2,DemonicLetters When there are no ``Fonts_Font_*`` lines in user's ``openmw.cfg``, built-in TrueType fonts are used. Font used by console and another debug windows is not configurable (so ``Fonts_Font_1`` is unused). @@ -20,8 +23,11 @@ You can use --export-fonts command line option to write the converted font They can be used instead of TrueType fonts if needed by specifying their ``.fnt`` files names in the ``openmw.cfg``. For example: - fallback=Fonts_Font_0,magic_cards_regular - fallback=Fonts_Font_2,daedric_font +.. code-block:: openmwcfg + :caption: openmw.cfg + + fallback=Fonts_Font_0,magic_cards_regular + fallback=Fonts_Font_2,daedric_font In this example OpenMW will search for ``magic_cards_regular.fnt`` and ``daedric_font.fnt`` in the ``Fonts`` folder in data directories. If they are not found, built-in TrueType fonts will be used as a fallback. @@ -35,16 +41,22 @@ Unlike vanilla Morrowind, OpenMW directly supports TrueType (``.ttf``) fonts. Th OpenMW has build-in TrueType fonts: MysticCards, DemonicLetters and DejaVuLGCSansMono, which are used by default. TrueType fonts are configured via ``openmw.cfg`` too: - fallback=Fonts_Font_0,MysticCards - fallback=Fonts_Font_2,DemonicLetters +.. code-block:: openmwcfg + :caption: openmw.cfg + + fallback=Fonts_Font_0,MysticCards + fallback=Fonts_Font_2,DemonicLetters In this example, OpenMW will scan ``Fonts`` folder in data directories for ``.omwfont`` files. These files are XML files with schema provided by MyGUI. OpenMW uses ``.omwfont`` files which name (without extension) matches ``openmw.cfg`` entries. -It is also possible to adjust the font size via ``settings.cfg`` file:: +It is also possible to adjust the font size via ``settings.cfg`` file - [GUI] - font size = 16 +.. code-block:: ini + :caption: settings.cfg + + [GUI] + font size = 16 The ``font size`` setting accepts clamped values in range from 12 to 18. diff --git a/docs/source/reference/modding/openmw-game-template.rst b/docs/source/reference/modding/openmw-game-template.rst index 68fa667911..51ddaf6efb 100644 --- a/docs/source/reference/modding/openmw-game-template.rst +++ b/docs/source/reference/modding/openmw-game-template.rst @@ -36,7 +36,8 @@ and ``data=`` tells OpenMW what folders to look for meshes, textures, audio, and other assets. The required lines would look like this, but with the paths of course different on your system. -.. code:: +.. code-block:: openmwcfg + :caption: openmw.cfg content=template.omwgame data="/home/someuser/example-suite/data" @@ -51,7 +52,8 @@ you need to remove or comment out the following lines from ``openmw.cfg``. Not doing so will either produce errors or load Morrowind content, which you probably do not want when you are making your own game. -.. code:: +.. code-block:: openmwcfg + :caption: openmw.cfg fallback-archive=Morrowind.bsa fallback-archive=Tribunal.bsa @@ -70,8 +72,10 @@ are instead assigned through ``settings.cfg``. These models are player and NPC animations, and meshes for the sky. In ``settings.cfg`` used by your OpenMW install, add the following lines under the ``[Models]`` section. -.. code:: +.. code-block:: ini + :caption: settings.cfg + [Models] xbaseanim = meshes/BasicPlayer.dae baseanim = meshes/BasicPlayer.dae xbaseanim1st = meshes/BasicPlayer.dae @@ -103,7 +107,7 @@ need to be copied to ``resources/mygui`` folder found in your OpenMW installatio folder. Overwrite any files aready in this folder. These files provide the UI font, its definition, and some minor UI tweaks. -.. code:: +.. code-block:: none openmw_box.skin.xml openmw_button.skin.xml diff --git a/docs/source/reference/modding/paths.rst b/docs/source/reference/modding/paths.rst index 4ea5219e74..0409777dc2 100644 --- a/docs/source/reference/modding/paths.rst +++ b/docs/source/reference/modding/paths.rst @@ -133,8 +133,9 @@ This can't change until computers are able to read minds. Lines with options have an option name, then an equals sign (``=``), then an option value. Option names and values have leading and trailing whitespace trimmed, but whitespace within an option value is preserved - it's only removed if it's at the ends. -This means that these are all equivalent: -:: +This means that these are all equivalent + +.. code-block:: openmwcfg data=some/dir data=some/dir @@ -226,7 +227,10 @@ Navigate to the OpenMW installation directory, and open the ``openmw.cfg`` file By default, this contains a warning at the top telling you that this is the local ``openmw.cfg`` and not to modify it. However, for this kind of install, it's okay to do so, so you can remove this warning. -Change the start of the file from:: +Change the start of the file from + +.. code-block:: openmwcfg + :caption: openmw.cfg # This is the local openmw.cfg file. Do not modify! # Modifications should be done on the user openmw.cfg file instead @@ -243,7 +247,10 @@ Change the start of the file from:: fallback=LightAttenuation_ConstantValue,0.0 fallback=LightAttenuation_UseLinear,1 -to:: +to + +.. code-block:: openmwcfg + :caption: openmw.cfg data-local=userdata/data user-data=userdata @@ -274,7 +281,10 @@ Navigate to the OpenMW installation directory, and open the ``openmw.cfg`` file By default, this contains a warning at the top telling you that this is the local ``openmw.cfg`` and not to modify it. However, you'll need to make a small change to create this kind of install. -Change the start of the file from:: +Change the start of the file from + +.. code-block:: openmwcfg + :caption: openmw.cfg # This is the local openmw.cfg file. Do not modify! # Modifications should be done on the user openmw.cfg file instead @@ -291,7 +301,10 @@ Change the start of the file from:: fallback=LightAttenuation_ConstantValue,0.0 fallback=LightAttenuation_UseLinear,1 -to:: +to + +.. code-block:: openmwcfg + :caption: openmw.cfg # This is the local openmw.cfg file. Do not modify! # Modifications should be done on the user openmw.cfg file instead @@ -330,7 +343,10 @@ From scratch Start by installing OpenMW in the usual way. Don't bother with first-time setup (i.e. telling it the location of an existing *Morrowind* installation). -In the default configuration directory (see `Configuration files and log files`_), create a file called ``openmw.cfg`` containing just:: +In the default configuration directory (see `Configuration files and log files`_), create a file called ``openmw.cfg`` containing just + +.. code-block:: openmwcfg + :caption: openmw.cfg # select the game profile config=Morrowind @@ -340,7 +356,10 @@ This will put the basic setup required to play *Morrowind* into a new ``Morrowin Next, come up with a name for the subprofile you'll create for your mod list. If you're following a modding guide, they've probably already given it a name, e.g. *Total Overhaul*, so that's the example we'll use. -Add a line to the ``Morrowind/openmw.cfg`` with the profile name, e.g.:: +Add a line to the ``Morrowind/openmw.cfg`` with the profile name, e.g. + +.. code-block:: openmwcfg + :caption: Morrowind/openmw.cfg # select the mod list profile config=Total Overhaul @@ -356,7 +375,10 @@ The ones in the ``Morrowind`` directory are used for all profiles for *Morrowind The ones in the ``Morrowind/Total Overhaul`` directory are only used for the *Total Overhaul* profile, so you can set up that mod list and any settings it requires here, and they won't affect any other profiles you set up later. Making changes within the launcher will affect these files and leave all the others alone. -If you want the *Total Overhaul* profile to keep its saved games etc. in a dedicated location instead of mixing them in with ones from another profile, you can add a ``user-data=…`` line to your ``Morrowind/Total Overhaul/openmw.cfg``, e.g.:: +If you want the *Total Overhaul* profile to keep its saved games etc. in a dedicated location instead of mixing them in with ones from another profile, you can add a ``user-data=…`` line to your ``Morrowind/Total Overhaul/openmw.cfg``, e.g. + +.. code-block:: openmwcfg + :caption: Morrowind/Total Overhaul/openmw.cfg # put saved games in a saves directory next to this file user-data=. @@ -377,12 +399,18 @@ You'll now have an empty directory e.g. at ``Documents\My Games\OpenMW\Original` Next, move all the files that were already in the default configuration directory to the profile directory you just made. Afterwards, the default configuration directory should only contain the profile directory you made. -Create a new ``openmw.cfg`` file in the default configuration directory containing:: +Create a new ``openmw.cfg`` file in the default configuration directory containing + +.. code-block:: openmwcfg + :caption: openmw.cfg # select the profile config=Original -In the ``openmw.cfg`` in the profile directory, add these lines:: +In the ``openmw.cfg`` in the profile directory, add these lines + +.. code-block:: openmwcfg + :caption: openmw.cfg data-local=data user-data=. @@ -402,9 +430,11 @@ Passing arguments on the command line lets you avoid this. The basic idea is that you need to pass ``--replace config`` to ignore the configuration directories that the engine would have loaded because they were specified in ``openmw.cfg`` files, and pass each one you want to use instead with ``--config ``. -E.g. if you've got a profile called *Morrowind* in your default configuration directory, and it's got a *Total Overhaul* subprofile, you could load it by running:: +E.g. if you've got a profile called *Morrowind* in your default configuration directory, and it's got a *Total Overhaul* subprofile, you could load it by running - openmw --replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul" +.. code-block:: console + + $ openmw --replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul" You can put this command into a script or shortcut and use it to easily launch OpenMW with that profile. @@ -422,15 +452,17 @@ On Windows, you can create a desktop shortcut to run this command with these ste * At the end of that field, add the arguments for the profile you want, e.g. ``--replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul"``. * Press *Apply* or *OK* to save the changes, and test the shortcut by double-clicking it. -On most Linux distros, you can create a ``.desktop`` file like this:: +On most Linux distros, you can create a ``.desktop`` file like this - [Desktop Entry] - Type=Application - Name=OpenMW - Total Overhaul - GenericName=Role Playing Game - Comment=OpenMW with the Total Overhaul profile - Keywords=Morrowind;Reimplementation Mods;esm;bsa; - TryExec=openmw - Exec=openmw --replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul" - Icon=openmw - Categories=Game;RolePlaying; +.. code-block:: desktop + + [Desktop Entry] + Type=Application + Name=OpenMW - Total Overhaul + GenericName=Role Playing Game + Comment=OpenMW with the Total Overhaul profile + Keywords=Morrowind;Reimplementation Mods;esm;bsa; + TryExec=openmw + Exec=openmw --replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul" + Icon=openmw + Categories=Game;RolePlaying; diff --git a/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst b/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst index e05177c268..89ea25519d 100644 --- a/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst +++ b/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst @@ -2,24 +2,9 @@ Normal maps from Morrowind to OpenMW ==================================== -- `General introduction to normal map conversion`_ - - `OpenMW normal-mapping`_ - - `Activating normal-mapping shaders in OpenMW`_ - - `Morrowind bump-mapping`_ - - `MGE XE normal-mapping`_ -- `Converting PeterBitt's Scamp Replacer`_ (Mod made for the MGE XE PBR prototype) - - `Tutorial - MGE`_ -- `Converting Lougian's Hlaalu Bump mapped`_ (Morrowind's bump-mapping, part 1: *without* custom models) - - `Tutorial - Morrowind, Part 1`_ -- `Converting Apel's Various Things - Sacks`_ (Morrowind's bump-mapping, part 2: *with* custom models) - - `Tutorial - Morrowind, Part 2`_ - General introduction to normal map conversion --------------------------------------------- -:Authors: Joakim (Lysol) Berg, Alexei (Capo) Dobrohotov -:Updated: 2020-03-03 - This page has general information and tutorials on how normal-mapping works in OpenMW and how you can make mods using the old environment-mapped bump-mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most (in)famous one to previously give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work better in OpenMW. @@ -58,14 +43,15 @@ Activating normal-mapping shaders in OpenMW Before normal (and specular and parallax) maps can show up in OpenMW, their auto-detection needs to be turned on in settings.cfg_-file. Add these rows where it would make sense: -:: +.. code-block:: ini + :caption: settings.cfg - [Shaders] - auto use object normal maps = true - auto use terrain normal maps = true + [Shaders] + auto use object normal maps = true + auto use terrain normal maps = true - auto use object specular maps = true - auto use terrain specular maps = true + auto use object specular maps = true + auto use terrain specular maps = true See OpenMW's wiki page about `texture modding`_ to read more about it. @@ -81,10 +67,11 @@ are processed which makes bump-mapped models look a bit better, can make use of the gloss map channel in the bump map and can apply bump-mapping to skinned models. Add this to settings.cfg_-file: -:: +.. code-block:: ini + :caption: settings.cfg - [Shaders] - apply lighting to environment maps = true + [Shaders] + apply lighting to environment maps = true But sometimes you may want them to look a bit better than in vanilla. Technically you aren't supposed to convert bump maps because they shouldn't be normal maps that are supported by OpenMW as well, @@ -117,9 +104,6 @@ Converting PeterBitt's Scamp Replacer ------------------------------------- **Mod made for the MGE XE PBR prototype** -:Authors: Joakim (Lysol) Berg -:Updated: 2016-11-11 - So, let's say you've found out that PeterBitt_ makes awesome models and textures featuring physically based rendering (PBR) and normal maps. Let's say that you tried to run his `PBR Scamp Replacer`_ in OpenMW and that you were greatly disappointed when the normal map didn't seem to work. Lastly, let's say you came here, looking for some answers. @@ -161,9 +145,6 @@ Converting Lougian's Hlaalu Bump mapped --------------------------------------- **Mod made for Morrowind's bump-mapping, without custom models** -:Authors: Joakim (Lysol) Berg, Alexei (Capo) Dobrohotov -:Updated: 2020-03-03 - Converting normal maps made for the Morrowind's bump-mapping can be really easy or a real pain, depending on a few circumstances. In this tutorial, we will look at a very easy, although in some cases a bit time-consuming, example. @@ -192,9 +173,6 @@ Converting Apel's Various Things - Sacks ---------------------------------------- **Mod made for Morrowind bump-mapping, with custom models** -:Authors: Joakim (Lysol) Berg, Alexei (Capostrophic) Dobrohotov -:Updated: 2020-03-03 - In part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``) files so that the bump maps could be loaded as normal maps. We ignored those model files since they are not needed with OpenMW. In this tutorial however, diff --git a/docs/source/reference/modding/texture-modding/texture-basics.rst b/docs/source/reference/modding/texture-modding/texture-basics.rst index 96ec48508b..2d85d1ed3b 100644 --- a/docs/source/reference/modding/texture-modding/texture-basics.rst +++ b/docs/source/reference/modding/texture-modding/texture-basics.rst @@ -55,17 +55,20 @@ Simply create the textures with appropriate naming convention the normal map would have to be called foo_n.dds). To enable this automatic use based on filename pattern, you will have to add the following to your -`settings.cfg `_ file:: +`settings.cfg `_ file - [Shaders] - auto use object normal maps = true +.. code-block:: ini + :caption: settings.cfg - auto use object specular maps = true + [Shaders] + auto use object normal maps = true - normal map pattern = _n - normal height map pattern = _nh + auto use object specular maps = true - specular map pattern = _spec + normal map pattern = _n + normal height map pattern = _nh + + specular map pattern = _spec Additionally, a normal map with the `_nh` pattern enables the use of the normal map's alpha channel as height information. @@ -92,18 +95,21 @@ For example, if you wanted to add specular mapping to a terrain layer called roc you would copy this texture to a new file called rock_diffusespec.dds, and then edit its alpha channel to set the specular intensity. -The relevant settings are:: +The relevant settings are - [Shaders] - auto use terrain normal maps = true +.. code-block:: ini + :caption: settings.cfg - auto use terrain specular maps = true + [Shaders] + auto use terrain normal maps = true - terrain specular map pattern = _diffusespec + auto use terrain specular maps = true - # Also used for terrain normal maps - normal map pattern = _n - normal height map pattern = _nh + terrain specular map pattern = _diffusespec + + # Also used for terrain normal maps + normal map pattern = _n + normal height map pattern = _nh OSG native files ################ From bc78aa4198b3f21636a7d8d1ad2fec19a7681e55 Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Fri, 4 Jul 2025 11:03:29 -0700 Subject: [PATCH 18/32] docs - add semicolons before examples --- docs/source/reference/modding/font.rst | 2 +- docs/source/reference/modding/paths.rst | 22 +++++++++---------- .../texture-modding/texture-basics.rst | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/source/reference/modding/font.rst b/docs/source/reference/modding/font.rst index b0bb342168..c0e91eec04 100644 --- a/docs/source/reference/modding/font.rst +++ b/docs/source/reference/modding/font.rst @@ -50,7 +50,7 @@ TrueType fonts are configured via ``openmw.cfg`` too: In this example, OpenMW will scan ``Fonts`` folder in data directories for ``.omwfont`` files. These files are XML files with schema provided by MyGUI. OpenMW uses ``.omwfont`` files which name (without extension) matches ``openmw.cfg`` entries. -It is also possible to adjust the font size via ``settings.cfg`` file +It is also possible to adjust the font size via ``settings.cfg`` file: .. code-block:: ini :caption: settings.cfg diff --git a/docs/source/reference/modding/paths.rst b/docs/source/reference/modding/paths.rst index 0409777dc2..c0f7d4cea6 100644 --- a/docs/source/reference/modding/paths.rst +++ b/docs/source/reference/modding/paths.rst @@ -133,7 +133,7 @@ This can't change until computers are able to read minds. Lines with options have an option name, then an equals sign (``=``), then an option value. Option names and values have leading and trailing whitespace trimmed, but whitespace within an option value is preserved - it's only removed if it's at the ends. -This means that these are all equivalent +This means that these are all equivalent: .. code-block:: openmwcfg @@ -227,7 +227,7 @@ Navigate to the OpenMW installation directory, and open the ``openmw.cfg`` file By default, this contains a warning at the top telling you that this is the local ``openmw.cfg`` and not to modify it. However, for this kind of install, it's okay to do so, so you can remove this warning. -Change the start of the file from +Change the start of the file from: .. code-block:: openmwcfg :caption: openmw.cfg @@ -247,7 +247,7 @@ Change the start of the file from fallback=LightAttenuation_ConstantValue,0.0 fallback=LightAttenuation_UseLinear,1 -to +to: .. code-block:: openmwcfg :caption: openmw.cfg @@ -281,7 +281,7 @@ Navigate to the OpenMW installation directory, and open the ``openmw.cfg`` file By default, this contains a warning at the top telling you that this is the local ``openmw.cfg`` and not to modify it. However, you'll need to make a small change to create this kind of install. -Change the start of the file from +Change the start of the file from: .. code-block:: openmwcfg :caption: openmw.cfg @@ -301,7 +301,7 @@ Change the start of the file from fallback=LightAttenuation_ConstantValue,0.0 fallback=LightAttenuation_UseLinear,1 -to +to: .. code-block:: openmwcfg :caption: openmw.cfg @@ -356,7 +356,7 @@ This will put the basic setup required to play *Morrowind* into a new ``Morrowin Next, come up with a name for the subprofile you'll create for your mod list. If you're following a modding guide, they've probably already given it a name, e.g. *Total Overhaul*, so that's the example we'll use. -Add a line to the ``Morrowind/openmw.cfg`` with the profile name, e.g. +Add a line to the ``Morrowind/openmw.cfg`` with the profile name like this: .. code-block:: openmwcfg :caption: Morrowind/openmw.cfg @@ -375,7 +375,7 @@ The ones in the ``Morrowind`` directory are used for all profiles for *Morrowind The ones in the ``Morrowind/Total Overhaul`` directory are only used for the *Total Overhaul* profile, so you can set up that mod list and any settings it requires here, and they won't affect any other profiles you set up later. Making changes within the launcher will affect these files and leave all the others alone. -If you want the *Total Overhaul* profile to keep its saved games etc. in a dedicated location instead of mixing them in with ones from another profile, you can add a ``user-data=…`` line to your ``Morrowind/Total Overhaul/openmw.cfg``, e.g. +If you want the *Total Overhaul* profile to keep its saved games etc. in a dedicated location instead of mixing them in with ones from another profile, you can add a ``user-data=…`` line to your ``Morrowind/Total Overhaul/openmw.cfg``, like this: .. code-block:: openmwcfg :caption: Morrowind/Total Overhaul/openmw.cfg @@ -399,7 +399,7 @@ You'll now have an empty directory e.g. at ``Documents\My Games\OpenMW\Original` Next, move all the files that were already in the default configuration directory to the profile directory you just made. Afterwards, the default configuration directory should only contain the profile directory you made. -Create a new ``openmw.cfg`` file in the default configuration directory containing +Create a new ``openmw.cfg`` file in the default configuration directory containing: .. code-block:: openmwcfg :caption: openmw.cfg @@ -407,7 +407,7 @@ Create a new ``openmw.cfg`` file in the default configuration directory containi # select the profile config=Original -In the ``openmw.cfg`` in the profile directory, add these lines +In the ``openmw.cfg`` in the profile directory, add these lines: .. code-block:: openmwcfg :caption: openmw.cfg @@ -430,7 +430,7 @@ Passing arguments on the command line lets you avoid this. The basic idea is that you need to pass ``--replace config`` to ignore the configuration directories that the engine would have loaded because they were specified in ``openmw.cfg`` files, and pass each one you want to use instead with ``--config ``. -E.g. if you've got a profile called *Morrowind* in your default configuration directory, and it's got a *Total Overhaul* subprofile, you could load it by running +E.g. if you've got a profile called *Morrowind* in your default configuration directory, and it's got a *Total Overhaul* subprofile, you could load it by running: .. code-block:: console @@ -452,7 +452,7 @@ On Windows, you can create a desktop shortcut to run this command with these ste * At the end of that field, add the arguments for the profile you want, e.g. ``--replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul"``. * Press *Apply* or *OK* to save the changes, and test the shortcut by double-clicking it. -On most Linux distros, you can create a ``.desktop`` file like this +On most Linux distros, you can create a ``.desktop`` file like this: .. code-block:: desktop diff --git a/docs/source/reference/modding/texture-modding/texture-basics.rst b/docs/source/reference/modding/texture-modding/texture-basics.rst index 2d85d1ed3b..26a47f4e0a 100644 --- a/docs/source/reference/modding/texture-modding/texture-basics.rst +++ b/docs/source/reference/modding/texture-modding/texture-basics.rst @@ -55,7 +55,7 @@ Simply create the textures with appropriate naming convention the normal map would have to be called foo_n.dds). To enable this automatic use based on filename pattern, you will have to add the following to your -`settings.cfg `_ file +`settings.cfg `_ file: .. code-block:: ini :caption: settings.cfg From 28de55df6acc5f6af88488cd76562a3e2f8e0e85 Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Fri, 4 Jul 2025 11:19:04 -0700 Subject: [PATCH 19/32] docs - use default pygment style in light mode --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index d6f19c38f1..0b953a4275 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -139,7 +139,7 @@ exclude_patterns = [] #show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = 'default' pygments_style_dark = 'github-dark' # A list of ignored prefixes for module index sorting. From ec3357ff3f9f233ceff676a9916fd224ca50f373 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Fri, 4 Jul 2025 20:30:31 +0200 Subject: [PATCH 20/32] Bump UI interface version number --- files/data/scripts/omw/ui.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/data/scripts/omw/ui.lua b/files/data/scripts/omw/ui.lua index 9d5133b3d0..31502fd6ee 100644 --- a/files/data/scripts/omw/ui.lua +++ b/files/data/scripts/omw/ui.lua @@ -171,7 +171,7 @@ return { interface = { --- Interface version -- @field [parent=#UI] #number version - version = 1, + version = 2, --- All available UI modes. -- Use `view(I.UI.MODE)` in `luap` console mode to see the list. From 4bf43665c4aa5268a5a1a5f63d5a14e954078fd2 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 27 Feb 2025 21:42:49 +0300 Subject: [PATCH 21/32] Revise birthsign tooltip layout (#6792) Make all description bits distinct widgets to allow proper padding Use spaces instead of line breaks to separate spells Enable word-wrapping for description text Make the tooltip wider and increase padding --- apps/openmw/mwgui/tooltips.cpp | 31 +++++++++++-------------- files/data/mygui/openmw_tooltips.layout | 26 +++++++++++++++++---- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 28f0b80010..7f8de572ed 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -894,7 +894,8 @@ namespace MWGui widget->setUserString("ToolTipLayout", "BirthSignToolTip"); widget->setUserString( "ImageTexture_BirthSignImage", Misc::ResourceHelpers::correctTexturePath(sign->mTexture, vfs)); - std::string text = sign->mName + "\n#{fontcolourhtml=normal}" + sign->mDescription; + widget->setUserString("Caption_BirthSignName", sign->mName); + widget->setUserString("Caption_BirthSignDescription", sign->mDescription); std::vector abilities, powers, spells; @@ -915,26 +916,22 @@ namespace MWGui spells.push_back(spell); } - using Category = std::pair&, std::string_view>; - for (const auto& [category, label] : std::initializer_list{ - { abilities, "sBirthsignmenu1" }, { powers, "sPowers" }, { spells, "sBirthsignmenu2" } }) + using Category = std::tuple&, std::string_view, std::string_view>; + std::initializer_list categories{ { abilities, "#{sBirthsignmenu1}", "Abilities" }, + { powers, "#{sPowers}", "Powers" }, { spells, "#{sBirthsignmenu2}", "Spells" } }; + + for (const auto& [category, label, widgetName] : categories) { - bool addHeader = true; - for (const ESM::Spell* spell : category) + std::string text; + if (!category.empty()) { - if (addHeader) - { - text += "\n\n#{fontcolourhtml=header}#{"; - text += label; - text += '}'; - addHeader = false; - } - - text += "\n#{fontcolourhtml=normal}" + spell->mName; + text = std::string(label) + "\n#{fontcolourhtml=normal}"; + for (const ESM::Spell* spell : category) + text += spell->mName + ' '; + text.pop_back(); } + widget->setUserString("Caption_BirthSign" + std::string(widgetName), text); } - - widget->setUserString("Caption_BirthSignText", text); } void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace) diff --git a/files/data/mygui/openmw_tooltips.layout b/files/data/mygui/openmw_tooltips.layout index ce927038c2..39cfbd06e1 100644 --- a/files/data/mygui/openmw_tooltips.layout +++ b/files/data/mygui/openmw_tooltips.layout @@ -272,16 +272,34 @@ - + - + - + - + + + + + + + + + + + + + + + + + + + From 2db9b91c1000e0ed154f4daf15f7321b4e2c8983 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 5 Jul 2025 17:10:02 +0400 Subject: [PATCH 22/32] Do not recreate animation object when harvesting a plant --- apps/openmw/mwrender/animation.cpp | 17 +++++++++++------ apps/openmw/mwrender/animation.hpp | 2 ++ apps/openmw/mwworld/actionharvest.cpp | 9 ++++++--- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f07a325f7c..c7ea225d5f 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -2096,12 +2096,17 @@ namespace MWRender if (Settings::game().mGraphicHerbalism && ptr.getRefData().getCustomData() != nullptr && ObjectAnimation::canBeHarvested()) { - const MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); - if (!store.hasVisibleItems()) - { - HarvestVisitor visitor; - mObjectRoot->accept(visitor); - } + harvest(ptr); + } + } + + void ObjectAnimation::harvest(const MWWorld::Ptr& ptr) + { + const MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); + if (!store.hasVisibleItems()) + { + HarvestVisitor visitor; + mObjectRoot->accept(visitor); } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index b6cb6f333c..6e39c44614 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -483,6 +483,7 @@ namespace MWRender virtual void setAccurateAiming(bool enabled) {} virtual bool canBeHarvested() const { return false; } + virtual void harvest(const MWWorld::Ptr& ptr) {} virtual void removeFromScene(); @@ -498,6 +499,7 @@ namespace MWRender bool animated, bool allowLight); bool canBeHarvested() const override; + void harvest(const MWWorld::Ptr& ptr) override; }; class UpdateVfxCallback : public SceneUtil::NodeCallback diff --git a/apps/openmw/mwworld/actionharvest.cpp b/apps/openmw/mwworld/actionharvest.cpp index 30f316c2db..1d9e009afe 100644 --- a/apps/openmw/mwworld/actionharvest.cpp +++ b/apps/openmw/mwworld/actionharvest.cpp @@ -11,6 +11,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" +#include "../mwrender/animation.hpp" + #include "class.hpp" #include "containerstore.hpp" @@ -89,8 +91,9 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->messageBox(tooltip); } - // Update animation object - MWBase::Environment::get().getWorld()->disable(target); - MWBase::Environment::get().getWorld()->enable(target); + auto world = MWBase::Environment::get().getWorld(); + MWRender::Animation* anim = world->getAnimation(target); + if (anim != nullptr) + anim->harvest(target); } } From 30a852bb883fcb49572154f2346963b10a2cb6f9 Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Sat, 5 Jul 2025 08:32:21 -0700 Subject: [PATCH 23/32] disable blending in second color attachment on water node --- apps/openmw/mwrender/water.cpp | 2 ++ components/resource/scenemanager.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 81e44248ac..81688a3444 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -565,6 +565,8 @@ namespace MWRender else createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat("Water_World_Alpha")); + mResourceSystem->getSceneManager()->setUpNormalsRTForStateSet(mWaterGeom->getOrCreateStateSet(), true); + updateVisible(); } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index cbda399120..7ee29ff0eb 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -607,6 +608,9 @@ namespace Resource if (!getSupportsNormalsRT()) return; stateset->setAttributeAndModes(new osg::ColorMaski(1, enabled, enabled, enabled, enabled)); + + if (enabled) + stateset->setAttributeAndModes(new osg::Disablei(GL_BLEND, 1)); } /// @brief Callback to read image files from the VFS. From 390589795cdbe7a0bea4c48eac665d4bd8986fce Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Fri, 4 Jul 2025 08:19:27 -0700 Subject: [PATCH 24/32] postprocessing - set mipmaps levels for rendertargets when requested --- apps/openmw/mwrender/postprocessor.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 1f1b7258b3..c3c98d7cef 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -655,6 +655,14 @@ namespace MWRender const auto [w, h] = renderTarget.mSize.get(renderWidth(), renderHeight()); subPass.mStateSet->setAttributeAndModes(new osg::Viewport(0, 0, w, h)); + if (subPass.mMipMap) + { + subPass.mRenderTexture->setNumMipmapLevels(osg::Image::computeNumberOfMipmapLevels(w, h)); + } + else + { + subPass.mRenderTexture->setNumMipmapLevels(0); + } subPass.mRenderTexture->setTextureSize(w, h); subPass.mRenderTexture->dirtyTextureObject(); From da71ec6e4951a73fe6ee34a8e06bbd1a41b2f418 Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Fri, 4 Jul 2025 08:39:39 -0700 Subject: [PATCH 25/32] postprocessing - bump revision --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 123462b8fd..c755ad315f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,7 @@ set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 50) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_LUA_API_REVISION 79) -set(OPENMW_POSTPROCESSING_API_REVISION 2) +set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_TAGHASH "") From bf92e551a7dfcc8f4117267dd8f479f39310d8c2 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 5 Jul 2025 18:09:36 +0100 Subject: [PATCH 26/32] Eliminate reference to vestigial MT_BUILD variable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c755ad315f..acbd42589f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -786,7 +786,7 @@ if (WIN32) endif() if (BUILD_BULLETOBJECTTOOL) - target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS} ${MT_BUILD}) + target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS}) endif() if (BUILD_OPENCS_TESTS) From da32ccee50a44745bebfa1e9badae3fe62998536 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 5 Jul 2025 19:49:08 +0100 Subject: [PATCH 27/32] Apply warning flags consistently for all compilers This avoids a problem where a bunch of our targets weren't having the right warning flags set up with MSVC. It shouldn't make any difference for other compilers, except Clang in clang-cl mode, which wants MSVC warning flags, and will now get them. It doesn't seem to resolve https://gitlab.com/OpenMW/openmw/-/issues/7882, so you still have to disable precompiled headers to see warnings with MSVC. --- CMakeLists.txt | 141 +++++++++++++------------------------------------ 1 file changed, 38 insertions(+), 103 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index acbd42589f..8e4880a88e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -590,30 +590,10 @@ if(OPENMW_LTO_BUILD) endif() endif() - -if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) - set(OPENMW_CXX_FLAGS "-Wall -Wextra -Wundef -Wextra-semi -Wno-unused-parameter -pedantic -Wno-long-long -Wnon-virtual-dtor -Wunused ${OPENMW_CXX_FLAGS}") - - if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105438 - set(OPENMW_CXX_FLAGS "-Wno-array-bounds ${OPENMW_CXX_FLAGS}") - endif() - - if (APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++") - endif() - - if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE) - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6) - set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-potentially-evaluated-expression") - endif () - endif() - - if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) - set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-unused-but-set-parameter -Wduplicated-branches -Wduplicated-cond -Wlogical-op") - endif() -endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) +if (APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++") +endif() # Extern @@ -624,8 +604,41 @@ if (BUILD_OPENCS OR BUILD_OPENCS_TESTS) add_subdirectory (extern/osgQt) endif() +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + add_compile_options("/W4") + + set(WARNINGS_DISABLE + 4100 # Unreferenced formal parameter (-Wunused-parameter) + 4127 # Conditional expression is constant + 4996 # Function was declared deprecated + 5054 # Deprecated operations between enumerations of different types caused by Qt headers + ) + + foreach(d ${WARNINGS_DISABLE}) + add_compile_options("/wd${d}") + endforeach(d) + + if(OPENMW_MSVC_WERROR) + add_compile_options("/WX") + endif() +else () + add_compile_options("-Wall" "-Wextra" "-Wundef" "-Wextra-semi" "-Wno-unused-parameter" "-pedantic" "-Wno-long-long" "-Wnon-virtual-dtor" "-Wunused") + + if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6) + add_compile_options("-Wno-potentially-evaluated-expression") + endif () + endif() + + if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) + add_compile_options("-Wno-unused-but-set-parameter" "-Wduplicated-branches" "-Wduplicated-cond" "-Wlogical-op") + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105438 + add_compile_options("-Wno-array-bounds") + endif() +endif () + if (OPENMW_CXX_FLAGS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENMW_CXX_FLAGS}") + add_compile_options(${OPENMW_CXX_FLAGS}) endif() # Components @@ -715,87 +728,9 @@ if (WIN32) set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") endif() - # Play a bit with the warning levels - - set(WARNINGS "/W4") - - set(WARNINGS_DISABLE - 4100 # Unreferenced formal parameter (-Wunused-parameter) - 4127 # Conditional expression is constant - 4996 # Function was declared deprecated - 5054 # Deprecated operations between enumerations of different types caused by Qt headers - ) - - foreach(d ${WARNINGS_DISABLE}) - list(APPEND WARNINGS "/wd${d}") - endforeach(d) - - if(OPENMW_MSVC_WERROR) - list(APPEND WARNINGS "/WX") - endif() - - target_compile_options(components PRIVATE ${WARNINGS}) - target_compile_options(osg-ffmpeg-videoplayer PRIVATE ${WARNINGS}) - if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920) target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE) endif() - - if (BUILD_BSATOOL) - target_compile_options(bsatool PRIVATE ${WARNINGS}) - endif() - - if (BUILD_ESMTOOL) - target_compile_options(esmtool PRIVATE ${WARNINGS}) - endif() - - if (BUILD_ESSIMPORTER) - target_compile_options(openmw-essimporter PRIVATE ${WARNINGS}) - endif() - - if (BUILD_LAUNCHER) - target_compile_options(openmw-launcher PRIVATE ${WARNINGS}) - endif() - - if (BUILD_MWINIIMPORTER) - target_compile_options(openmw-iniimporter PRIVATE ${WARNINGS}) - endif() - - if (BUILD_OPENCS) - target_compile_options(openmw-cs PRIVATE ${WARNINGS}) - endif() - - if (BUILD_OPENMW) - target_compile_options(openmw PRIVATE ${WARNINGS}) - endif() - - if (BUILD_WIZARD) - target_compile_options(openmw-wizard PRIVATE ${WARNINGS}) - endif() - - if (BUILD_COMPONENTS_TESTS) - target_compile_options(components-tests PRIVATE ${WARNINGS}) - endif() - - if (BUILD_BENCHMARKS) - target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE ${WARNINGS}) - endif() - - if (BUILD_NAVMESHTOOL) - target_compile_options(openmw-navmeshtool PRIVATE ${WARNINGS}) - endif() - - if (BUILD_BULLETOBJECTTOOL) - target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS}) - endif() - - if (BUILD_OPENCS_TESTS) - target_compile_options(openmw-cs-tests PRIVATE ${WARNINGS}) - endif() - - if (BUILD_OPENMW_TESTS) - target_compile_options(openmw-tests PRIVATE ${WARNINGS}) - endif() endif(MSVC) # TODO: At some point release builds should not use the console but rather write to a log file From c7e3f9b0cf47a0797572b61b0f0396592df64838 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Fri, 4 Jul 2025 21:38:14 +0200 Subject: [PATCH 28/32] Add some events from the dehardcode spellcasting MR, that do not need to be specific to that MR. --- .../source/reference/lua-scripting/events.rst | 70 +++++++++++++++++++ files/data/CMakeLists.txt | 2 + files/data/builtin.omwscripts | 2 + .../scripts/omw/mechanics/actorcontroller.lua | 23 ++++++ .../omw/mechanics/globalcontroller.lua | 39 +++++++++++ .../omw/mechanics/playercontroller.lua | 6 ++ 6 files changed, 142 insertions(+) create mode 100644 files/data/scripts/omw/mechanics/actorcontroller.lua create mode 100644 files/data/scripts/omw/mechanics/globalcontroller.lua diff --git a/docs/source/reference/lua-scripting/events.rst b/docs/source/reference/lua-scripting/events.rst index 007e0e43d1..d6ccd93003 100644 --- a/docs/source/reference/lua-scripting/events.rst +++ b/docs/source/reference/lua-scripting/events.rst @@ -41,9 +41,53 @@ Example: core.sendGlobalEvent('UseItem', {object = potion, actor = player, force = true}) +**ModifyStat** + +Modify the corresponding stat. + +.. code-block:: Lua + + -- Consume 10 magicka + actor:sendEvent('ModifyStat', {name = 'magicka', amount = -10}) + +**AddVfx** + +Calls the corresponding method in openmw.animation + +.. code-block:: Lua + + local eventParams = { + model = 'vfx_default', + options = { + textureOverride = effect.particle, + }, + } + actor:sendEvent('AddVfx', eventParams) + +**PlaySound3d** + +Calls the corresponding function in openw.core on the target. Will use core.sound.playSoundFile3d instead of core.sound.playSound3d if you put `file` instead of `sound` in the event data. + +.. code-block:: Lua + actor:sendEvent('PlaySound3d', {sound = 'Open Lock'}) + + +**BreakInvisibility** + +Forces the actor to lose all active invisibility effects. + + UI events --------- +**ShowMessage** + +If sent to a player, shows a message as if a call to ui.showMessage was made. + +.. code-block:: Lua + + player:sendEvent('ShowMessage', {message = 'Lorem ipsum'}) + **UiModeChanged** Every time UI mode is changed built-in scripts send to player the event ``UiModeChanged`` with arguments ``oldMode, ``newMode`` (same as ``I.UI.getMode()``) @@ -91,3 +135,29 @@ Global events that just call the corresponding function in `openmw.world`. -- world.setSimulationTimeScale(scale) core.sendGlobalEvent('SetSimulationTimeScale', scale) + + +**SpawnVfx, PlaySound3d** + +Calls the corresponding function in openw.core. Note that PlaySound3d will call core.sound.playSoundFile3d instead of core.sound.playSound3d if you put `file` instead of `sound` in the event data. + +.. code-block:: Lua + core.sendGlobalEvent('SpawnVfx', {position = hitPos, model = 'vfx_destructarea', options = {scale = 10}}) + core.sendGlobalEvent('PlaySound3d', {sound = 'Open Lock', position = container.position}) + +**ConsumeItem** + +Reduces stack size of an item by a given amount, removing the item complete if removing all items in the stack are removed. + +.. code-block:: Lua + + core.sendGlobalEvent('ConsumeItem', {item = foobar, amount = 1}) + +**ModifyItemCharge** + +Modify a specified amount of enchantment charge of an item + +.. code-block:: Lua + + -- Reduce charge by 10 + core.sendGlobalEvent('ModifyItemCharge', {item = foobar, amount = -10}) diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index d9218b45b2..39715af75d 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -91,7 +91,9 @@ set(BUILTIN_DATA_FILES scripts/omw/console/local.lua scripts/omw/console/player.lua scripts/omw/console/menu.lua + scripts/omw/mechanics/actorcontroller.lua scripts/omw/mechanics/animationcontroller.lua + scripts/omw/mechanics/globalcontroller.lua scripts/omw/mechanics/playercontroller.lua scripts/omw/settings/menu.lua scripts/omw/music/actor.lua diff --git a/files/data/builtin.omwscripts b/files/data/builtin.omwscripts index 37367783ab..6126c0b761 100644 --- a/files/data/builtin.omwscripts +++ b/files/data/builtin.omwscripts @@ -23,6 +23,8 @@ PLAYER: scripts/omw/input/actionbindings.lua PLAYER: scripts/omw/input/smoothmovement.lua PLAYER: scripts/omw/input/gamepadcontrols.lua NPC,CREATURE: scripts/omw/ai.lua +GLOBAL: scripts/omw/mechanics/globalcontroller.lua +CREATURE, NPC, PLAYER: scripts/omw/mechanics/actorcontroller.lua # User interface PLAYER: scripts/omw/ui.lua diff --git a/files/data/scripts/omw/mechanics/actorcontroller.lua b/files/data/scripts/omw/mechanics/actorcontroller.lua new file mode 100644 index 0000000000..fe8c75b244 --- /dev/null +++ b/files/data/scripts/omw/mechanics/actorcontroller.lua @@ -0,0 +1,23 @@ +local self = require('openmw.self') +local core = require('openmw.core') +local types = require('openmw.types') +local Actor = types.Actor + +return { + eventHandlers = { + ModifyStat = function(data) + local stat = Actor.stats.dynamic[data.stat](self) + stat.current = stat.current + data.amount + end, + PlaySound3d = function(data) + if data.sound then + core.sound.playSound3d(data.sound, self, data.options) + else + core.sound.playSoundFile3d(data.file, self, data.options) + end + end, + BreakInvisibility = function(data) + Actor.activeEffects(self):remove(core.magic.EFFECT_TYPE.Invisibility) + end, + }, +} diff --git a/files/data/scripts/omw/mechanics/globalcontroller.lua b/files/data/scripts/omw/mechanics/globalcontroller.lua new file mode 100644 index 0000000000..22d92e7c24 --- /dev/null +++ b/files/data/scripts/omw/mechanics/globalcontroller.lua @@ -0,0 +1,39 @@ +local types = require('openmw.types') +local Lockable = types.Lockable +local Item = require('openmw.types').Item +local world = require('openmw.world') +local core = require('openmw.core') + +local function onConsumeItem(data) + local item = data.item + local amount = data.amount + if amount > item.count then + print('Warning: tried to consume '..tostring(amount)..' '..tostring(item)..'s, but there were only '..tostring(item.count)) + amount = item.count + end + item:remove(amount) +end + +local function onPlaySound3d(data) + if data.sound then + core.sound.playSound3d(data.sound, data.position, data.options) + elseif data.file then + core.sound.playSoundFile3d(data.file, data.position, data.options) + end +end + +return { + eventHandlers = { + SpawnVfx = function(data) + world.vfx.spawn(data.model, data.position, data.options) + end, + PlaySound3d = onPlaySound3d, + ConsumeItem = onConsumeItem, + Lock = function(data) + Lockable.lock(data.target, data.magnitude) + end, + Unlock = function(data) + Lockable.unlock(data.target) + end, + }, +} diff --git a/files/data/scripts/omw/mechanics/playercontroller.lua b/files/data/scripts/omw/mechanics/playercontroller.lua index 870f24415c..8b4d618917 100644 --- a/files/data/scripts/omw/mechanics/playercontroller.lua +++ b/files/data/scripts/omw/mechanics/playercontroller.lua @@ -111,4 +111,10 @@ return { engineHandlers = { onUpdate = onUpdate, }, + + eventHandlers = { + ShowMessage = function(data) + if data.message then ui.showMessage(data.message) end + end + }, } From 1b9802472da6b446b1f5fd2c2af0c4a1a30fd016 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 5 Jul 2025 01:43:39 +0200 Subject: [PATCH 29/32] Doc updates --- docs/source/reference/lua-scripting/events.rst | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/source/reference/lua-scripting/events.rst b/docs/source/reference/lua-scripting/events.rst index d6ccd93003..a4683b0dc2 100644 --- a/docs/source/reference/lua-scripting/events.rst +++ b/docs/source/reference/lua-scripting/events.rst @@ -147,7 +147,7 @@ Calls the corresponding function in openw.core. Note that PlaySound3d will call **ConsumeItem** -Reduces stack size of an item by a given amount, removing the item complete if removing all items in the stack are removed. +Reduces stack size of an item by a given amount, removing the item completely if stack size is reduced to 0 or less. .. code-block:: Lua @@ -161,3 +161,19 @@ Modify a specified amount of enchantment charge of an item -- Reduce charge by 10 core.sendGlobalEvent('ModifyItemCharge', {item = foobar, amount = -10}) + +**Lock** + +Lock a container or door + +.. code-block:: Lua + + core.sendGlobalEvent('Lock', {taret = selected, magnitude = 50}) + +**Unlock** + +Unlock a container or door + +.. code-block:: Lua + + core.sendGlobalEvent('Unlock', {taret = selected}) From 7143115e57011fca4f3fbbcb2af8d53116173929 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 5 Jul 2025 13:09:27 +0200 Subject: [PATCH 30/32] Doc update --- docs/source/reference/lua-scripting/events.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/source/reference/lua-scripting/events.rst b/docs/source/reference/lua-scripting/events.rst index a4683b0dc2..15c9b52eea 100644 --- a/docs/source/reference/lua-scripting/events.rst +++ b/docs/source/reference/lua-scripting/events.rst @@ -153,15 +153,6 @@ Reduces stack size of an item by a given amount, removing the item completely if core.sendGlobalEvent('ConsumeItem', {item = foobar, amount = 1}) -**ModifyItemCharge** - -Modify a specified amount of enchantment charge of an item - -.. code-block:: Lua - - -- Reduce charge by 10 - core.sendGlobalEvent('ModifyItemCharge', {item = foobar, amount = -10}) - **Lock** Lock a container or door From c2535903386f3bdfd7ff164456443a9dd053a062 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 5 Jul 2025 22:56:42 +0100 Subject: [PATCH 31/32] Restore previous interpretation of OPENMW_CXX_FLAGS It used to be copied as-is into the command-line options for the compiler, whereas add_compile_options expects a list of arguments. separate_arguments can be used to split a string how the system would split a command line. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e4880a88e..f5a7d55bd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -638,6 +638,7 @@ else () endif () if (OPENMW_CXX_FLAGS) + separate_arguments(OPENMW_CXX_FLAGS NATIVE_COMMAND "${OPENMW_CXX_FLAGS}") add_compile_options(${OPENMW_CXX_FLAGS}) endif() From 45c187028fe23fde4eba4f021badb70c3ec402cd Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 5 Jul 2025 13:25:22 +0200 Subject: [PATCH 32/32] Bump --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c755ad315f..3a194450fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 50) set(OPENMW_VERSION_RELEASE 0) -set(OPENMW_LUA_API_REVISION 79) +set(OPENMW_LUA_API_REVISION 80) set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "")