From ffec2e8d74799acd6c7d9d856b98998ad613726e Mon Sep 17 00:00:00 2001 From: Andy Lanzone Date: Fri, 4 Jul 2025 14:38:55 -0700 Subject: [PATCH 001/102] Automatically show Xbox, PS, or Switch button icons --- apps/openmw/mwbase/inputmanager.hpp | 2 + .../openmw/mwgui/controllerbuttonsoverlay.cpp | 24 ++++ .../openmw/mwgui/controllerbuttonsoverlay.hpp | 1 + apps/openmw/mwgui/inventorytabsoverlay.cpp | 12 ++ apps/openmw/mwgui/inventorywindow.cpp | 7 +- apps/openmw/mwgui/tradewindow.cpp | 8 +- apps/openmw/mwinput/controllermanager.cpp | 114 ++++++++++++++++++ apps/openmw/mwinput/controllermanager.hpp | 5 + apps/openmw/mwinput/inputmanagerimp.cpp | 10 ++ apps/openmw/mwinput/inputmanagerimp.hpp | 2 + files/data/CMakeLists.txt | 13 ++ files/data/mygui/openmw_inventory_tabs.layout | 4 +- files/data/textures/omw_psx_button_circle.dds | Bin 0 -> 22000 bytes files/data/textures/omw_psx_button_dpad.dds | Bin 0 -> 22000 bytes files/data/textures/omw_psx_button_square.dds | Bin 0 -> 22000 bytes .../data/textures/omw_psx_button_triangle.dds | Bin 0 -> 22000 bytes files/data/textures/omw_psx_button_x.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_l1.dds | Bin 22000 -> 22000 bytes files/data/textures/omw_steam_button_r1.dds | Bin 22000 -> 22000 bytes files/data/textures/omw_switch_button_l.dds | Bin 0 -> 22000 bytes files/data/textures/omw_switch_button_r.dds | Bin 0 -> 22000 bytes files/data/textures/omw_switch_button_zl.dds | Bin 0 -> 22000 bytes files/data/textures/omw_switch_button_zr.dds | Bin 0 -> 22000 bytes files/data/textures/omw_xbox_button_lb.dds | Bin 0 -> 22000 bytes files/data/textures/omw_xbox_button_lt.dds | Bin 0 -> 22000 bytes files/data/textures/omw_xbox_button_rb.dds | Bin 0 -> 22000 bytes files/data/textures/omw_xbox_button_rt.dds | Bin 0 -> 22000 bytes 27 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 files/data/textures/omw_psx_button_circle.dds create mode 100644 files/data/textures/omw_psx_button_dpad.dds create mode 100644 files/data/textures/omw_psx_button_square.dds create mode 100644 files/data/textures/omw_psx_button_triangle.dds create mode 100644 files/data/textures/omw_psx_button_x.dds create mode 100644 files/data/textures/omw_switch_button_l.dds create mode 100644 files/data/textures/omw_switch_button_r.dds create mode 100644 files/data/textures/omw_switch_button_zl.dds create mode 100644 files/data/textures/omw_switch_button_zr.dds create mode 100644 files/data/textures/omw_xbox_button_lb.dds create mode 100644 files/data/textures/omw_xbox_button_lt.dds create mode 100644 files/data/textures/omw_xbox_button_rb.dds create mode 100644 files/data/textures/omw_xbox_button_rt.dds diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index f5adc42340..4358c7a1e0 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -79,6 +79,8 @@ namespace MWBase /// @return true if joystick, false otherwise virtual bool joystickLastUsed() = 0; virtual void setJoystickLastUsed(bool enabled) = 0; + virtual std::string getControllerButtonIcon(int button) = 0; + virtual std::string getControllerAxisIcon(int axis) = 0; virtual int countSavedGameRecords() const = 0; virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) = 0; diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index 9466f40105..065fb46d77 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -1,6 +1,7 @@ #include "controllerbuttonsoverlay.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" namespace MWGui @@ -8,50 +9,67 @@ namespace MWGui ControllerButtonsOverlay::ControllerButtonsOverlay() : WindowBase("openmw_controllerbuttons.layout") { + MWBase::InputManager* inputMgr = MWBase::Environment::get().getInputManager(); + getWidget(mImageA, "BtnAImage"); getWidget(mTextA, "BtnAText"); + setIcon(mImageA, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_A)); getWidget(mImageB, "BtnBImage"); getWidget(mTextB, "BtnBText"); + setIcon(mImageB, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_B)); getWidget(mImageDpad, "BtnDpadImage"); getWidget(mTextDpad, "BtnDpadText"); + setIcon(mImageDpad, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_DPAD_UP)); getWidget(mImageL1, "BtnL1Image"); getWidget(mTextL1, "BtnL1Text"); + setIcon(mImageL1, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); getWidget(mImageL2, "BtnL2Image"); getWidget(mTextL2, "BtnL2Text"); + setIcon(mImageL2, inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERLEFT)); getWidget(mImageL3, "BtnL3Image"); getWidget(mTextL3, "BtnL3Text"); + setIcon(mImageL3, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_LEFTSTICK)); getWidget(mImageLStick, "BtnLStickImage"); getWidget(mTextLStick, "BtnLStickText"); + setIcon(mImageLStick, inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_LEFTY)); getWidget(mImageMenu, "BtnMenuImage"); getWidget(mTextMenu, "BtnMenuText"); + setIcon(mImageMenu, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_BACK)); getWidget(mImageR1, "BtnR1Image"); getWidget(mTextR1, "BtnR1Text"); + setIcon(mImageR1, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); getWidget(mImageR2, "BtnR2Image"); getWidget(mTextR2, "BtnR2Text"); + setIcon(mImageR2, inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERRIGHT)); getWidget(mImageR3, "BtnR3Image"); getWidget(mTextR3, "BtnR3Text"); + setIcon(mImageR3, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_RIGHTSTICK)); getWidget(mImageRStick, "BtnRStickImage"); getWidget(mTextRStick, "BtnRStickText"); + setIcon(mImageRStick, inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_RIGHTY)); getWidget(mImageView, "BtnViewImage"); getWidget(mTextView, "BtnViewText"); + setIcon(mImageView, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_START)); getWidget(mImageX, "BtnXImage"); getWidget(mTextX, "BtnXText"); + setIcon(mImageX, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_X)); getWidget(mImageY, "BtnYImage"); getWidget(mTextY, "BtnYText"); + setIcon(mImageY, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_Y)); getWidget(mHBox, "ButtonBox"); } @@ -79,6 +97,12 @@ namespace MWGui setVisible(buttonCount > 0); } + void ControllerButtonsOverlay::setIcon(MyGUI::ImageBox* image, const std::string& imagePath) + { + if (imagePath.length() > 0) + image->setImageTexture(imagePath); + } + int ControllerButtonsOverlay::updateButton( MyGUI::TextBox* text, MyGUI::ImageBox* image, const std::string& buttonStr) { diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index 7008384bee..700cf0c147 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -65,6 +65,7 @@ namespace MWGui Gui::HBox* mHBox; + void setIcon(MyGUI::ImageBox* image, const std::string& imagePath); int updateButton(MyGUI::TextBox* text, MyGUI::ImageBox* image, const std::string& buttonStr); }; } diff --git a/apps/openmw/mwgui/inventorytabsoverlay.cpp b/apps/openmw/mwgui/inventorytabsoverlay.cpp index 35b4d1d87f..062e7dbcb4 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.cpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.cpp @@ -1,6 +1,9 @@ #include "inventorytabsoverlay.hpp" +#include + #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" namespace MWGui @@ -25,6 +28,15 @@ namespace MWGui getWidget(tab, "TabStats"); tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); mTabs.push_back(tab); + + MyGUI::ImageBox* image; + getWidget(image, "BtnL2Image"); + image->setImageTexture( + MWBase::Environment::get().getInputManager()->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERLEFT)); + + getWidget(image, "BtnR2Image"); + image->setImageTexture( + MWBase::Environment::get().getInputManager()->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERRIGHT)); } void InventoryTabsOverlay::onTabClicked(MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index a398139df4..9ffed95aa1 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -19,6 +19,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/luamanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" @@ -133,14 +134,18 @@ namespace MWGui if (Settings::gui().mControllerMenus) { // Show L1 and R1 buttons next to tabs - MyGUI::Widget* image; + MyGUI::ImageBox* image; getWidget(image, "BtnL1Image"); image->setVisible(true); image->setUserString("Hidden", "false"); + image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon( + SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); getWidget(image, "BtnR1Image"); image->setVisible(true); image->setUserString("Hidden", "false"); + image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon( + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); mControllerButtons.r3 = "#{sInfo}"; } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index c26a7f030a..3e6e886b9d 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -11,6 +12,7 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" @@ -172,14 +174,18 @@ namespace MWGui if (Settings::gui().mControllerMenus) { // Show L1 and R1 buttons next to tabs - MyGUI::Widget* image; + MyGUI::ImageBox* image; getWidget(image, "BtnL1Image"); image->setVisible(true); image->setUserString("Hidden", "false"); + image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon( + SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); getWidget(image, "BtnR1Image"); image->setVisible(true); image->setUserString("Hidden", "false"); + image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon( + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); mControllerButtons.a = "#{sBuy}"; mControllerButtons.b = "#{sCancel}"; diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 9962c639ad..68cd9a7dbd 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -465,6 +465,120 @@ namespace MWInput return std::array({ gyro[0], gyro[1], gyro[2] }); } + int ControllerManager::getControllerType() + { + int type = 0; +#if SDL_VERSION_ATLEAST(2, 0, 12) + SDL_GameController* cntrl = mBindingsManager->getControllerOrNull(); + if (cntrl) + type = SDL_GameControllerGetType(cntrl); +#endif + return type; + } + + std::string ControllerManager::getControllerButtonIcon(int button) + { + int controllerType = ControllerManager::getControllerType(); + + bool isXbox = false; + bool isPsx = false; + bool isSwitch = false; + +#if SDL_VERSION_ATLEAST(2, 0, 12) + isXbox = controllerType == SDL_CONTROLLER_TYPE_XBOX360 || controllerType == SDL_CONTROLLER_TYPE_XBOXONE; + isPsx = controllerType == SDL_CONTROLLER_TYPE_PS3 || controllerType == SDL_CONTROLLER_TYPE_PS4 + || controllerType == SDL_CONTROLLER_TYPE_PS5; + isSwitch = controllerType == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; +#endif + + switch (button) + { + case SDL_CONTROLLER_BUTTON_A: + if (isPsx) + return "textures/omw_psx_button_x.dds"; + return "textures/omw_steam_button_a.dds"; + case SDL_CONTROLLER_BUTTON_B: + if (isPsx) + return "textures/omw_psx_button_circle.dds"; + return "textures/omw_steam_button_b.dds"; + case SDL_CONTROLLER_BUTTON_BACK: + return "textures/omw_steam_button_view.dds"; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + case SDL_CONTROLLER_BUTTON_DPAD_UP: + if (isPsx) + return "textures/omw_psx_button_dpad.dds"; + return "textures/omw_steam_button_dpad.dds"; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: + if (isXbox) + return "textures/omw_xbox_button_lb.dds"; + else if (isSwitch) + return "textures/omw_switch_button_l.dds"; + return "textures/omw_steam_button_l1.dds"; + case SDL_CONTROLLER_BUTTON_LEFTSTICK: + return "textures/omw_steam_button_l3.dds"; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: + if (isXbox) + return "textures/omw_xbox_button_rb.dds"; + else if (isSwitch) + return "textures/omw_switch_button_r.dds"; + return "textures/omw_steam_button_r1.dds"; + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: + return "textures/omw_steam_button_r3.dds"; + case SDL_CONTROLLER_BUTTON_START: + return "textures/omw_steam_button_menu.dds"; + case SDL_CONTROLLER_BUTTON_X: + if (isPsx) + return "textures/omw_psx_button_square.dds"; + return "textures/omw_steam_button_x.dds"; + case SDL_CONTROLLER_BUTTON_Y: + if (isPsx) + return "textures/omw_psx_button_triangle.dds"; + return "textures/omw_steam_button_y.dds"; + case SDL_CONTROLLER_BUTTON_GUIDE: + default: + return ""; + } + } + + std::string ControllerManager::getControllerAxisIcon(int axis) + { + int controllerType = ControllerManager::getControllerType(); + + bool isXbox = false; + bool isSwitch = false; + +#if SDL_VERSION_ATLEAST(2, 0, 12) + isXbox = controllerType == SDL_CONTROLLER_TYPE_XBOX360 || controllerType == SDL_CONTROLLER_TYPE_XBOXONE; + isSwitch = controllerType == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; +#endif + + switch (axis) + { + case SDL_CONTROLLER_AXIS_LEFTX: + case SDL_CONTROLLER_AXIS_LEFTY: + return "textures/omw_steam_button_lstick.dds"; + case SDL_CONTROLLER_AXIS_RIGHTX: + case SDL_CONTROLLER_AXIS_RIGHTY: + return "textures/omw_steam_button_rstick.dds"; + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: + if (isXbox) + return "textures/omw_xbox_button_lt.dds"; + else if (isSwitch) + return "textures/omw_switch_button_zl.dds"; + return "textures/omw_steam_button_l2.dds"; + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + if (isXbox) + return "textures/omw_xbox_button_rt.dds"; + else if (isSwitch) + return "textures/omw_switch_button_zr.dds"; + return "textures/omw_steam_button_r2.dds"; + default: + return ""; + } + } + void ControllerManager::touchpadMoved(int deviceId, const SDLUtil::TouchEvent& arg) { MWBase::Environment::get().getLuaManager()->inputEvent({ MWBase::LuaManager::InputEvent::TouchMoved, arg }); diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp index 670a3c846f..535ee85fd5 100644 --- a/apps/openmw/mwinput/controllermanager.hpp +++ b/apps/openmw/mwinput/controllermanager.hpp @@ -48,6 +48,9 @@ namespace MWInput bool isGyroAvailable() const; std::array getGyroValues() const; + std::string getControllerButtonIcon(int button); + std::string getControllerAxisIcon(int axis); + private: // Return true if GUI consumes input. bool gamepadToGuiControl(const SDL_ControllerButtonEvent& arg); @@ -55,6 +58,8 @@ namespace MWInput void enableGyroSensor(); + int getControllerType(); + BindingsManager* mBindingsManager; MouseManager* mMouseManager; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 12d56a3321..7a82c96d09 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -249,6 +249,16 @@ namespace MWInput return mControllerManager->joystickLastUsed(); } + std::string InputManager::getControllerButtonIcon(int button) + { + return mControllerManager->getControllerButtonIcon(button); + } + + std::string InputManager::getControllerAxisIcon(int axis) + { + return mControllerManager->getControllerAxisIcon(axis); + } + void InputManager::executeAction(int action) { mActionManager->executeAction(action); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index b2899e6831..27a938f1ea 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -92,6 +92,8 @@ namespace MWInput void setJoystickLastUsed(bool enabled) override; bool joystickLastUsed() override; + std::string getControllerButtonIcon(int button) override; + std::string getControllerAxisIcon(int axis) override; int countSavedGameRecords() const override; void write(ESM::ESMWriter& writer, Loading::Listener& progress) override; diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index b1da9f6756..6038e8cdc8 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -10,6 +10,11 @@ set(BUILTIN_DATA_FILES textures/omw_menu_scroll_center_h.dds textures/omw_menu_scroll_center_v.dds textures/omw_menu_icon_active.dds + textures/omw_psx_button_circle.dds + textures/omw_psx_button_dpad.dds + textures/omw_psx_button_square.dds + textures/omw_psx_button_triangle.dds + textures/omw_psx_button_x.dds textures/omw_steam_button_a.dds textures/omw_steam_button_b.dds textures/omw_steam_button_dpad.dds @@ -25,6 +30,14 @@ set(BUILTIN_DATA_FILES textures/omw_steam_button_view.dds textures/omw_steam_button_x.dds textures/omw_steam_button_y.dds + textures/omw_switch_button_l.dds + textures/omw_switch_button_r.dds + textures/omw_switch_button_zl.dds + textures/omw_switch_button_zr.dds + textures/omw_xbox_button_lb.dds + textures/omw_xbox_button_lt.dds + textures/omw_xbox_button_rb.dds + textures/omw_xbox_button_rt.dds textures/omw/water_nm.png fonts/DejaVuFontLicense.txt diff --git a/files/data/mygui/openmw_inventory_tabs.layout b/files/data/mygui/openmw_inventory_tabs.layout index 3787452586..d13361d5db 100644 --- a/files/data/mygui/openmw_inventory_tabs.layout +++ b/files/data/mygui/openmw_inventory_tabs.layout @@ -10,7 +10,7 @@ - + @@ -39,7 +39,7 @@ - + diff --git a/files/data/textures/omw_psx_button_circle.dds b/files/data/textures/omw_psx_button_circle.dds new file mode 100644 index 0000000000000000000000000000000000000000..53d9856a28011ef55ca3eb49f745a6488a02a33c GIT binary patch literal 22000 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 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_psx_button_dpad.dds b/files/data/textures/omw_psx_button_dpad.dds new file mode 100644 index 0000000000000000000000000000000000000000..b5f74b5153538c9234b86e68dd16357bbf306601 GIT binary patch literal 22000 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 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_psx_button_x.dds b/files/data/textures/omw_psx_button_x.dds new file mode 100644 index 0000000000000000000000000000000000000000..dc032dfedcba0bd87a7aa01cafa8f8eb1ebf1b35 GIT binary patch literal 22000 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 I6!LiXe`A%mtpET3 diff --git a/files/data/textures/omw_steam_button_r1.dds b/files/data/textures/omw_steam_button_r1.dds index bdd93de2b2975c69b7d431ad106dcbad015f1b02..8474bb9ff8b0106303ce1a53ba329ee546417f7f 100644 GIT binary patch literal 22000 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 B0Q~>} literal 22000 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 C_0qNg diff --git a/files/data/textures/omw_switch_button_l.dds b/files/data/textures/omw_switch_button_l.dds new file mode 100644 index 0000000000000000000000000000000000000000..ee6ed5cd3f5aa2e3c83119f2b6e89a8180d12203 GIT binary patch literal 22000 zcmeHP4^UG_8sC?YNKiu%qGvOiP}JAS3=lC>lPd?8knw7(S+~ zr^x?C`$~k&DEKTY%zyM&=S6FbH~(89{!4&^{E{#cgCq}0A0&GK2}u6n9`OeV<-$Sv zCY{D#H~0_6*gF&y6liHa!CDS+N_%;?z1)Z~E~-K%YhUZ~80t?G01F$wR8g$sOjm^s z4{;RhF|L@50&uy6N0JAT-=V2xG@pyJPi@;?yN+SZ$B~7{Fds%tBC}`si3baQcr^Y+ zzO{-Q{K_f6&|VR;ypu-TGjgWJiyNO&Q(Ubw@!hi>jPHXwM|H~sDU9!<51Wcx`jQ#n z$LE`hyStS>yg-^;9-t@tNuR3CwKR7(i_3`}u zHD6J_Z(EeF-IrmY^*NVU4XkT;gz?LtNL%(~ zd?MplxccB1Dj8zfBdoC{nXE~)JW}9a;(^_hgsC(z1EJ;N5lnmhh!nZUp#+0@cmhH{S12kj+kMaE&Utj&&}65-uIxh@0yzEqv)wNEGW9yr@41iDIz# zBAuc1C5rQ94x6oNBlGXQ3#;5q`_D3e&Yv3SQzbmi>>cd+jKnvV-tF1zc=Laa@b65Z z*HhLzP^*SSu|A0ZIOMSH-CEDi->Yffy)oUz&i7ac)-P%7VEnuJvZLNpO}M(-@=xR4 zU#WgV1>+-J{*%$x-J4818UKHqoud_XElQ z2hRT_`={13G5mWd|0&tOWd8{e0>WC2B@t@=FWJ9j|Ni5DY5X^_^+EFgqx=6~mD-D7 zeFBRQRlkhPVd0A>EBXFPp8D5N_v5>rFY!M*|H1QtaB=I2&nvxp2~ybSSQ2>x9o zP8&0yxd@CKT^T>zK6sfwNve;?c+kCO|99KJMtXY^|DtW;Z*nJy@ObT=)~9g!*9FCY zdrO1k{}B9-;P~&Imfu9L57vLU^8M!jq1J!I8Q|fceHB~((X$vH{#{PUqxK_QNXX?H zHA^Ul^+xdg_l&fDD?$cObU%8zit68&z+2W&lV&th2o_3WJaG(XM&i@$bev-Ll6 z{ZR2i`wCd^C;H37(+1b({idEhA9rtx?@$dU)YE+aG)gq}y}|PNuK_3RX8OSQN_br5 z5iD=ahwhn7%8z@KMS1AiQ;hG!(+X2P2lH6IT)+FduI;Iee^~!jmIYH{T0VnIud_KC z`Y7I`Zn(6r)yC#Kt+S>XE9^Fwe>?*nDYrK;{*Nz^@VLq&NZzfpEPt;1dK1lj7@$Es>V)!0mzvsQ0FYbpbhxrlN@2cQpVQ{3}2m2{7=8${_ zjyHF}{U2j=1J_0j*1~??mEs3Y~Q2jvpUa2o9X>(8-o3i>$m(I_s1magtQ6;*C%r{*wTN^`YZen_BZ+MUmN1hR$Lc8h)qOze6b()gFIjT{VVi( z-CSdd?^0qr-}b(?~CZ? z|K9HXvL<5pdxR^GejWxt)kADOteo8cefwFrp{lxFB;HNzjq~W`ZKm0~FH(Q(%?o^< z;mwb59CW=NJxYG9^go%jKikm1vQkJqnDp1kDGjAGKByos^m(V&OWx0b^9u^(GfXmZ zKjp;4TBNz2;@2P9BnW-7y2vBcZnXbFHVK$hvnk@55%L6C-X&^^N{&3iknU_ zjF0~}%&BqthGVq+pYZ#k95%2|Jpba5pln{fB;FTK5jHsHnBeND{#pjf%>zRy0q;u; zfvbb^1)1x>JQPb7h~e^rY@T-VduwI1K94Z^{$|qX`z_u%MB;39R4(CvK&6sbc#-qU{T5AVh0z>Fr+s zSlbuSQ~S?j-k|@v@K<@=@RvjI$}o&c=qki`A-H`(mafAb*x0+&Z|^%!w* zeS!>>Z;$VJET{gwnEE3Q{88L=x*i!iHx8In&s;;>GeZ3y93R>`b264+0SJ)Cn7n@r zFkHedu`YHWIUnv*w)Ih z=Y`H!*7)8(CrptW%x88%d=ELo3D3#mgdp`QfA0IpO0I6ThfB!>#7S>l0{}*eh B@qPdR literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_switch_button_r.dds b/files/data/textures/omw_switch_button_r.dds new file mode 100644 index 0000000000000000000000000000000000000000..f2d5060ca15c39b2f9b025beab69a4a59469abdc GIT binary patch literal 22000 zcmeHP3vg7`89sM6**qZG4M}8hyAw)~j1~;Ex>kbjmdDhqh3t?`OHpeGQ6n`8T88?9 z8-b}hMe>j~ykab1bp}BMyAGw}M#C_o)kIopAhEz2Vi|>G2}{DV+0*}=n{)2nE3jaY zgm~}VP4?UKxaa@B|NQ5Dr%%rt{R~1V&XS1A;XnBxC;5X=Jbc8NKl}L%_=wZ)p}$@1 z&u9pYfzR|g?px0nFW%z(=FhS6PZ%!xO~+IXx;}J!(CY(eK=%jV6Muk^%Yu+#@|3iA z<`t9LFM#Aiil+os+U?9AS_SbGUYsK^Y$Ld27}BxqFVC-!B(5K-zpY7kF@2>W@swk0 z(u)iy03I7Kzs0c3$Ao&4yNg&Nua5}jyPc6ZKi|#5NmP3E#b=O(ll)@gBKceGMSo<^ z%MrwvTX#K~%J916eR%iH|4CrjJ6|1uzWvIkcGQ|$KQEuO+3vGlDoCIx17baMXj=SSNT(w&_DAf8!0 zR;(V&;-gsdO}?m&>6odbZ#>1}d2tYztX1n1pPFxc5k_$&713vS&!)_`%1zu~`0xE1 z>?0A{XE{9i)ztcgW2+x$_$F!(MvXm8IIlf``;W0su@`2-&C>gdj#nnb&9FaQysCY{ zwswYfzKJH}>uT705I<70SykWg!MVn*DtjpC+TfaXW6f$(B2u2!iM`Vt8!J9z_&Suk zKdWo`oeV4fK#p(I@L%kkv;0=LS^O@-KimxK{1Z*c*VW)(tM4Ed*Q)Iy{PA4l$K~vzk>F?_Ane@7k0agN@9vGO~nV>5yw>$k=dHcwQg zH#g$LA^Ss0evOcGZ&@45zrTE4>Ga?a)~Wjs;YN$`L{sAHth^uIyfJg~9EJaJXwi?e z7F@ghODi0q2Dq7g+V<>9V`+tFUW>{;9`ND{e4y-0<@w~Bd<_PFay*>icjpEckrI*e zw7S+od#>=GW?ylCbCvBN3%}Ot|K;-UkbpdUA?J(Yln^D1=qIBN!aX+L9!9gD>_&TO zj^|-3)8Cb@&;8BM5A8u(ZotjT=bIndpR?vXYiov^g%5T9r4{zlFW_eKYV~XGXbi>S zZm$wftOD`zS2TO@?tzp&6(l*WZ`%B?HtwSQ`_ou?ihYseIolMSe@a##Um0)Wt~%@| zt$ZJU7w13Y0pc&_<*@jT|L!4Q=|xix@cf6)en<$s{PK8aGgbk9o}bwPo@7D zt-hP&O))P|eWNc}G1?Yp@HfM-XT@r}(XqsbE(*6M$t zvCe-a|BXnjo{~^&QTU1s*YclTLHc)!Kgj5Sl)V+S!$?yOuz!zcUpoJh{A&Au8Q!;- zG*|Rqa(i#~{aFW=EG3gcPir{tv}FW z^Wk7e@8UAPUZU0iKx3W%i}SDL$I3Ldv>@Obb{P$NsLp{CDzg+j2e;Dd~YvsFK{-f{zfXyHJ`ad^FqdZZ{`ICeCNbPP0~Y`0W>01~#SV*qe^T~;7;R??w|E|!#=^IaIJq$I zv6|fscR!KZlyEYwhT-rhXtdb&XC59t3V7^VZVzFfj=xiVX!T9_u>RV?`00M4+_ZJ> z$DHqNBj!v9Zpq`}M%&u^557e91IqRb`$dbV!H|aG9O1UA_4{hu7+#lLd*pHNdOrW< z9Y4ZZ>RHdj8*GAXzhR1wzf*l^^z9vA{&v;hdb$4BB{xkz5WrcCzZ~I}T*uN= zJX|oXp3uBqncq~#x5S%ST!QHj4_WE_C-rWEkTT!+h^c_vhgC=|cn8<<{A>bh^5}TL z^kEgs4;)hIUr4#}wgmJj=Swi94wu)rD4)(qzGGq)%-S;=Pqn_GW1<08pLBic_MqE? zULS^Z# zh1>&-?-Hl(P@SLaPM6+4v_Ia->f^ge^kuza{*+E;&(&;Y~uY(8+7vU5?4d;VY=f~O%OQ$+Es`Oz7eJm&Yf8lJ1 z%jJxL{o-o*1K|%ULU6PE2AjOm$^-KuYt;MU@qI2hAETU?TZ(4whw~3`9!h@A%9WNs zw@2!UlRD#={^DyygM2=Lu#=y!#mCV4%2s;5OAaT+68x2OWHzBn_H#WEx0V||K=I|Q zJbMjYo9;Yf1o`DBTygnfeO)cTwmrduSiNdgB|lG!p}vk?untYejXB2(?rbAI_wDyvoDN5pC#LZ)mPEJ*PJR&n_!>T5G=7=A;l1T#JPyP=UG5LT zAMX*@{D9!}^jHBO5M0Qh?r)Kj%NMbjX!a2pWb)rn+N&oY|0(4=-|4&$@E);p55=*u z=~%e^s9>1L+n;FiMGGHn^5d9!FC8DXhTFgJ8<>3CLH3^j-)^@HqYTS1v1$du&?3jm z^SjHqy+o5&8-CU0@1gbWIbxwig7})q{u8!<{f|=4lVkLhVO*v0c@mp6&xRV$gVw(O z7IelCPBInLne)EwVX5ai=?q^F z`@{&1-;qtc{acBitXf0oyNR_TA!sw!i#M-5%DR0n*#=Tf{!olQO ziT*b&i_QmAd7XmEUc}}HV_^W4j{aI1QuRlSBP07S*BVYomfkFtJcc-!{O+6Ic7eRw z{>k?Z3&~`>STdgYU!snb>*)9q+TU-7lT%6hnIl+wA%Wn&@fR6DYvvcQVV5;GYoUSJ zr*x7!e*$UyQGS^iFP6rLVA?D_HU&0)TCTibWe0h`(h_q1m5hI!D6_jDxgBm6U+bUO z!$}VZ?#^K0v`}=ti`E}E#jjRABbOHZhh}{oaK!msZ15RxcNyu1HIhTr=f8se8RUkG z#1Ei-wP|8iD?u@E32*KgIp^6Mpnng677M~HF%}dzn#-@1=ZD>9zXfiV-{n5P3HobO z#P#4$<>wnt>@5t`kAeLI;5=ahp@*J~xp04d%;i467yBjLMT$!#L@}Yx9h1Z`MdV$V z82w_^A@UzW;q_$*mzykP9)rc#%CCJsHh}T@SMO%=m!5=g;PM!j`iZsqVFNOEu2a@y zh2*`3%pVC*0aLC+1q_o4d8M(CJnB)FzZ3Eglm7UZzohzu+KDKp@AHECe;ynp@fR6x zC;HBZ7m_VH6FFaCIhT9t?(8##On>+$w9xQEXFSIf>R~)Oyq;lg`s8!jorP}(d4D&x zw_w$=yt+0ee&l#AjA9p-a{rD%9$0_AIR3j|6+ZlHRPR~R{416}M$#C+v3MTC!@pkg r*=vs~@3$L%>Z6u_lq7Kb@ZIe(&T3ZHM_+zDrXJMB!)rGZq~QMp3nKn1 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_switch_button_zl.dds b/files/data/textures/omw_switch_button_zl.dds new file mode 100644 index 0000000000000000000000000000000000000000..c23c54afcc5f073956359604b524d9a957a51e5f GIT binary patch literal 22000 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 IUnT$l1KC_E00000 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_xbox_button_lb.dds b/files/data/textures/omw_xbox_button_lb.dds new file mode 100644 index 0000000000000000000000000000000000000000..ae73e5014a104929f44e53f1472a953b27ae8221 GIT binary patch literal 22000 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 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_xbox_button_rb.dds b/files/data/textures/omw_xbox_button_rb.dds new file mode 100644 index 0000000000000000000000000000000000000000..335ece93a6e6f6e6c9850b427f2f447f15f3ca1e GIT binary patch literal 22000 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 T=_}TT)Pfl~|9R_A81eZ3{a_6< literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_xbox_button_rt.dds b/files/data/textures/omw_xbox_button_rt.dds new file mode 100644 index 0000000000000000000000000000000000000000..fe7987310cf6e39b20e7bed3d08ee2af8cbe2986 GIT binary patch literal 22000 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 UqEL!A<)ac(GQ}J6gnAhMKL~ Date: Fri, 4 Jul 2025 23:06:05 -0700 Subject: [PATCH 002/102] Make topic padding a const and use it when scrolling dialog topics --- apps/openmw/mwgui/dialogue.cpp | 13 +++++-------- apps/openmw/mwgui/dialogue.hpp | 3 +++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index b3f83f3771..0b6648be6c 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -653,13 +653,10 @@ namespace MWGui if (mTopicsList->getItemCount() > 0) mTopicsList->addSeparator(); - // Morrowind uses 3 px invisible borders for padding topics - constexpr int verticalPadding = 3; - for (const auto& keyword : mKeywords) { std::string topicId = Misc::StringUtils::lowerCase(keyword); - mTopicsList->addItem(keyword, verticalPadding); + mTopicsList->addItem(keyword, sVerticalPadding); auto t = std::make_unique(keyword); mKeywordSearch.seed(topicId, intptr_t(t.get())); @@ -947,18 +944,18 @@ namespace MWGui if (focused) { // Scroll the side bar to keep the active item in view - if (index <= 8) + if (index <= 6) mTopicsList->setViewOffset(0); else { int offset = 0; - for (int i = 0; i < static_cast(index) - 8; i++) + for (int i = 0; i < static_cast(index) - 6; i++) { const std::string& keyword = mTopicsList->getItemNameAt(i); if (keyword.empty()) - offset += 21; + offset += 18 + sVerticalPadding * 2; else - offset += mTopicsList->getItemWidget(keyword)->getHeight() + 3; + offset += mTopicsList->getItemWidget(keyword)->getHeight() + sVerticalPadding * 2; } mTopicsList->setViewOffset(-offset); } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 6f03076e92..90f1c3eaaf 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -226,6 +226,9 @@ namespace MWGui MyGUI::IntSize mCurrentWindowSize; + // Morrowind uses 3 px invisible borders for padding topics + static const int sVerticalPadding = 3; + std::unique_ptr mCallback; std::unique_ptr mGreetingCallback; From dfa3b5f7edb59ce85ece8b54bc4c9163f404e27b Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 4 Jul 2025 23:09:31 -0700 Subject: [PATCH 003/102] Add Russian translation for controller menu launcher checkboxes --- files/lang/launcher_ru.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/files/lang/launcher_ru.ts b/files/lang/launcher_ru.ts index cd6768942f..7f0400670d 100644 --- a/files/lang/launcher_ru.ts +++ b/files/lang/launcher_ru.ts @@ -1464,19 +1464,19 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov <html><head/><body><p>Make it easier to use game menus with a controller.</p></body></html> - + <html><head/><body><p>Упрощает использование игровых меню на геймпаде.</p></body></html> Enable Controller Menus - + Интерфейс для геймпадов <html><head/><body><p>When using controller menus, make tooltips visible by default.</p></body></html> - + <html><head/><body><p>Показывать подсказки интерфейса для геймпадов по умолчанию.</p></body></html> Show Controller Tooltips By Default - + Подсказки для геймпадов по умолчанию From b6f3b2760a77c14521f939595b5a42e783dd4317 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 5 Jul 2025 10:27:45 -0700 Subject: [PATCH 004/102] Use a new string for 'Select Character' controller button label --- apps/openmw/mwgui/savegamedialog.cpp | 4 ++-- files/data/l10n/OMWEngine/de.yaml | 3 ++- files/data/l10n/OMWEngine/en.yaml | 3 ++- files/data/l10n/OMWEngine/fr.yaml | 3 ++- files/data/l10n/OMWEngine/ru.yaml | 3 ++- files/data/l10n/OMWEngine/sv.yaml | 3 ++- files/data/mygui/openmw_savegame_dialog.layout | 2 +- 7 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index e0b10f6b37..e335601e95 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -251,7 +251,7 @@ namespace MWGui } mCharacterSelection->setIndexSelected(selectedIndex); if (selectedIndex == MyGUI::ITEM_NONE) - mCharacterSelection->setCaptionWithReplacing("#{OMWEngine:SelectCharacter}..."); + mCharacterSelection->setCaptionWithReplacing("#{OMWEngine:SelectCharacter}"); fillSaveList(); } @@ -528,7 +528,7 @@ namespace MWGui ControllerButtonStr* SaveGameDialog::getControllerButtons() { - mControllerButtons.y = mSaving ? "" : "#{OMWEngine:SelectCharacter}"; + mControllerButtons.y = mSaving ? "" : "#{OMWEngine:LoadingSelectCharacter}"; return &mControllerButtons; } diff --git a/files/data/l10n/OMWEngine/de.yaml b/files/data/l10n/OMWEngine/de.yaml index 25bc145159..782ae50824 100644 --- a/files/data/l10n/OMWEngine/de.yaml +++ b/files/data/l10n/OMWEngine/de.yaml @@ -58,7 +58,7 @@ MissingContentFilesListCopy: |- other{\n\nDrücken Sie „Kopieren“, um alle Namen in die Zwischenablage zu kopieren.} } OverwriteGameConfirmation: "Sind Sie sicher, dass Sie den Spielstand überschreiben wollen?" -SelectCharacter: "Charakter auswählen" +SelectCharacter: "Charakter auswählen..." TimePlayed: "Spielzeit" @@ -221,5 +221,6 @@ EnchantType: "Zaubertyp" InventorySelect: "Geben" JournalQuests: "Quests" JournalShowAll: "Alle Anzeigen" +LoadingSelectCharacter: "Charakter auswählen" RechargeSelect: "Aufladen" RepairTool: "Werkzeug" diff --git a/files/data/l10n/OMWEngine/en.yaml b/files/data/l10n/OMWEngine/en.yaml index 176e604f8e..4ec744ce80 100644 --- a/files/data/l10n/OMWEngine/en.yaml +++ b/files/data/l10n/OMWEngine/en.yaml @@ -58,7 +58,7 @@ MissingContentFilesListCopy: |- other{\n\nPress Copy to place their names to the clipboard.} } OverwriteGameConfirmation: "Are you sure you want to overwrite this saved game?" -SelectCharacter: "Select Character" +SelectCharacter: "Select Character..." TimePlayed: "Time Played" @@ -220,5 +220,6 @@ EnchantType: "Cast Type" InventorySelect: "Put" JournalQuests: "Quests" JournalShowAll: "Show All" +LoadingSelectCharacter: "Change Character" RechargeSelect: "Recharge" RepairTool: "Tool" diff --git a/files/data/l10n/OMWEngine/fr.yaml b/files/data/l10n/OMWEngine/fr.yaml index bd96ae0d80..808d15b4d8 100644 --- a/files/data/l10n/OMWEngine/fr.yaml +++ b/files/data/l10n/OMWEngine/fr.yaml @@ -58,7 +58,7 @@ MissingContentFilesListCopy: |- other{\n\nCliquez sur Copier pour placer ces noms dans le presse-papier.} } OverwriteGameConfirmation: "Écraser la sauvegarde précédente ?" -SelectCharacter: "Sélection du personnage" +SelectCharacter: "Sélection du personnage..." TimePlayed: "Temps de jeu" @@ -220,5 +220,6 @@ EnchantType: "Type de lancement" InventorySelect: "Placer" JournalQuests: "Quêtes" JournalShowAll: "Tout Afficher" +LoadingSelectCharacter: "Sélection du personnage" RechargeSelect: "Recharge" RepairTool: "Outil" diff --git a/files/data/l10n/OMWEngine/ru.yaml b/files/data/l10n/OMWEngine/ru.yaml index 6bee247c9d..6a4e265791 100644 --- a/files/data/l10n/OMWEngine/ru.yaml +++ b/files/data/l10n/OMWEngine/ru.yaml @@ -58,7 +58,7 @@ MissingContentFilesListCopy: |- other{\n\nНажмите Скопировать, чтобы поместить их названия в буфер обмена.} } OverwriteGameConfirmation: "Вы уверены, что хотите перезаписать это сохранение?" -SelectCharacter: "Выберите персонажа" +SelectCharacter: "Выберите персонажа..." TimePlayed: "Время в игре" @@ -220,5 +220,6 @@ EnchantType: "Тип заклинания" InventorySelect: "Поместить" JournalQuests: "Квесты" JournalShowAll: "Показать все" +LoadingSelectCharacter: "Выбрать персонажа" RechargeSelect: "Перезарядить" RepairTool: "Инструмент" diff --git a/files/data/l10n/OMWEngine/sv.yaml b/files/data/l10n/OMWEngine/sv.yaml index 50a33582f3..1617c65956 100644 --- a/files/data/l10n/OMWEngine/sv.yaml +++ b/files/data/l10n/OMWEngine/sv.yaml @@ -59,7 +59,7 @@ MissingContentFilesListCopy: |- other{\n\nKlicka på kopiera för att placera deras namn i urklipp.} } OverwriteGameConfirmation: "Är du säker på att du vill skriva över det här sparade spelet?" -SelectCharacter: "Välj spelfigur" +SelectCharacter: "Välj spelfigur..." # Settings menu @@ -221,5 +221,6 @@ EnchantType: "Typ" InventorySelect: "Placera" JournalQuests: "Uppdrag" JournalShowAll: "Visa Alla" +LoadingSelectCharacter: "Välj spelfigur" RechargeSelect: "Ladda" RepairTool: "Verktyg" diff --git a/files/data/mygui/openmw_savegame_dialog.layout b/files/data/mygui/openmw_savegame_dialog.layout index 5a45810943..8bb6a64401 100644 --- a/files/data/mygui/openmw_savegame_dialog.layout +++ b/files/data/mygui/openmw_savegame_dialog.layout @@ -6,7 +6,7 @@ - + From abe04679159511e858867eafe6f3d4fe0ab7df2a Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 6 Jul 2025 22:15:59 -0700 Subject: [PATCH 005/102] Show count dialog when dropping stacks (cleans up the logic too) --- apps/openmw/mwgui/inventorywindow.cpp | 94 +++++++++++++++------------ apps/openmw/mwgui/inventorywindow.hpp | 11 ++++ 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 9ffed95aa1..a19f04c73a 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -91,6 +91,7 @@ namespace MWGui , mPreview(std::make_unique(parent, resourceSystem, MWMechanics::getPlayer())) , mTrading(false) , mUpdateTimer(0.f) + , mPendingControllerAction(ControllerAction::None) { mPreviewTexture = std::make_unique(mPreview->getTexture(), mPreview->getTextureStateSet()); @@ -331,18 +332,23 @@ namespace MWGui // Show a dialog to select a count of items, but not when using an item from the inventory // in controller mode. In that case, we skip the dialog and just use one item immediately. - if (count > 1 && !shift && !(Settings::gui().mControllerMenus && mGuiMode == MWGui::GM_Inventory)) + if (count > 1 && !shift && mPendingControllerAction != ControllerAction::Use) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - std::string message = mTrading ? "#{sQuanityMenuMessage01}" : "#{sTake}"; + std::string message = "#{sTake}"; + if (mTrading || mPendingControllerAction == ControllerAction::Sell) + message = "#{sQuanityMenuMessage01}"; + else if (mPendingControllerAction == ControllerAction::Drop) + message = "#{sDrop}"; std::string name{ object.getClass().getName(object) }; name += MWGui::ToolTips::getSoulString(object.getCellRef()); dialog->openCountDialog(name, message, count); dialog->eventOkClicked.clear(); - if (Settings::gui().mControllerMenus - && (mGuiMode == MWGui::GM_Companion || mGuiMode == MWGui::GM_Container)) + if (mPendingControllerAction == ControllerAction::Give) dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::giveItem); - else if (mTrading) + else if (mPendingControllerAction == ControllerAction::Drop) + dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dropItem); + else if (mTrading || mPendingControllerAction == ControllerAction::Sell) dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem); else dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dragItem); @@ -351,11 +357,27 @@ namespace MWGui else { mSelectedItem = index; - if (mTrading) + + if (mPendingControllerAction == ControllerAction::Use) + { + // Drag and drop the item on the avatar to activate it. + dragItem(nullptr, count); + onAvatarClicked(nullptr); // Equip or use + // Drop any remaining items back in inventory. This is needed when clicking on a + // stack of items; we only want to use the first item. + onBackgroundSelected(); + } + else if (mPendingControllerAction == ControllerAction::Give) + giveItem(nullptr, count); + else if (mPendingControllerAction == ControllerAction::Drop) + dropItem(nullptr, count); + else if (mTrading || mPendingControllerAction == ControllerAction::Sell) sellItem(nullptr, count); else dragItem(nullptr, count); } + + mPendingControllerAction = ControllerAction::None; } void InventoryWindow::ensureSelectedItemUnequipped(int count) @@ -424,9 +446,8 @@ namespace MWGui void InventoryWindow::giveItem(MyGUI::Widget* sender, int count) { - ensureSelectedItemUnequipped(count); - mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count); - notifyContentChanged(); + if (!mDragAndDrop->mIsOnDragAndDrop) + dragItem(sender, count); if (mGuiMode == MWGui::GM_Companion && mDragAndDrop->mIsOnDragAndDrop) { @@ -448,6 +469,19 @@ namespace MWGui } } + void InventoryWindow::dropItem(MyGUI::Widget* sender, int count) + { + if (mGuiMode != MWGui::GM_Inventory) + return; + + if (!mDragAndDrop->mIsOnDragAndDrop) + dragItem(sender, count); + + // Drop the item into the gameworld + if (mDragAndDrop->mIsOnDragAndDrop) + MWBase::Environment::get().getWindowManager()->getHud()->dropDraggedItem(0.5f, 0.5f); + } + void InventoryWindow::updateItemView() { MWBase::Environment::get().getWindowManager()->updateSpellWindow(); @@ -964,51 +998,29 @@ namespace MWGui bool InventoryWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { + mPendingControllerAction = ControllerAction::None; // Clear any pending controller actions + if (arg.button == SDL_CONTROLLER_BUTTON_B) { MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } else if (arg.button == SDL_CONTROLLER_BUTTON_A) { + if (mGuiMode == MWGui::GM_Inventory) + mPendingControllerAction = ControllerAction::Use; + else if (mGuiMode == MWGui::GM_Companion || mGuiMode == MWGui::GM_Container) + mPendingControllerAction = ControllerAction::Give; + else if (mGuiMode == MWGui::GM_Barter) + mPendingControllerAction = ControllerAction::Sell; + mItemView->onControllerButton(SDL_CONTROLLER_BUTTON_A); - // The following actions are done here, not in onItemSelectedFromSourceModel, because we - // want the mouse to work even in controller mode. - if (mGuiMode == MWGui::GM_Inventory && mDragAndDrop->mIsOnDragAndDrop) - { - // Drag and drop the item on the avatar to activate it. - onAvatarClicked(nullptr); // Equip or use - // Drop any remaining items back in inventory. This is needed when clicking on a - // stack of items; we only want to use the first item. - onBackgroundSelected(); - } - else if (mGuiMode == MWGui::GM_Companion && mDragAndDrop->mIsOnDragAndDrop) - { - // Drag and drop the item on the companion's window. - MWGui::CompanionWindow* companionWindow = (MWGui::CompanionWindow*)MWBase::Environment::get() - .getWindowManager() - ->getGuiModeWindows(mGuiMode) - .at(1); - mDragAndDrop->drop(companionWindow->getModel(), companionWindow->getItemView()); - } - else if (mGuiMode == MWGui::GM_Container && mDragAndDrop->mIsOnDragAndDrop) - { - // Drag and drop the item on the container window. - MWGui::ContainerWindow* containerWindow = (MWGui::ContainerWindow*)MWBase::Environment::get() - .getWindowManager() - ->getGuiModeWindows(mGuiMode) - .at(0); - mDragAndDrop->drop(containerWindow->getModel(), containerWindow->getItemView()); - } - // GM_Barter is handled by onControllerButtonEvent. No other steps are necessary. } else if (arg.button == SDL_CONTROLLER_BUTTON_X) { if (mGuiMode == MWGui::GM_Inventory) { - // Drop the item into the gameworld + mPendingControllerAction = ControllerAction::Drop; mItemView->onControllerButton(SDL_CONTROLLER_BUTTON_A); - if (mDragAndDrop->mIsOnDragAndDrop) - MWBase::Environment::get().getWindowManager()->getHud()->dropDraggedItem(0.5f, 0.5f); } else if (mGuiMode == MWGui::GM_Container) { diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index a14e0f9477..1a800d9762 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -120,9 +120,20 @@ namespace MWGui void onBackgroundSelected(); + enum ControllerAction + { + None = 0, + Use, + Give, + Sell, + Drop, + }; + ControllerAction mPendingControllerAction; + void sellItem(MyGUI::Widget* sender, int count); void dragItem(MyGUI::Widget* sender, int count); void giveItem(MyGUI::Widget* sender, int count); + void dropItem(MyGUI::Widget* sender, int count); void onWindowResize(MyGUI::Window* _sender); void onFilterChanged(MyGUI::Widget* _sender); From e349fa248aaaddcf625a741796b1d2e8d8ec0cd3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 6 Jul 2025 22:39:13 -0700 Subject: [PATCH 006/102] Tweak how scroll offsets are calculated for smoother scrolling --- apps/openmw/mwgui/dialogue.cpp | 21 ++++++++------------- apps/openmw/mwgui/journalwindow.cpp | 13 ++++--------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 0b6648be6c..0d694a6c4d 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -944,21 +944,16 @@ namespace MWGui if (focused) { // Scroll the side bar to keep the active item in view - if (index <= 6) - mTopicsList->setViewOffset(0); - else + int offset = 0; + for (int i = 6; i < static_cast(index); i++) { - int offset = 0; - for (int i = 0; i < static_cast(index) - 6; i++) - { - const std::string& keyword = mTopicsList->getItemNameAt(i); - if (keyword.empty()) - offset += 18 + sVerticalPadding * 2; - else - offset += mTopicsList->getItemWidget(keyword)->getHeight() + sVerticalPadding * 2; - } - mTopicsList->setViewOffset(-offset); + const std::string& keyword = mTopicsList->getItemNameAt(i); + if (keyword.empty()) + offset += 18 + sVerticalPadding * 2; + else + offset += mTopicsList->getItemWidget(keyword)->getHeight() + sVerticalPadding * 2; } + mTopicsList->setViewOffset(-offset); } } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 8302f4faea..e956696221 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -971,15 +971,10 @@ namespace // Scroll the list to keep the active item in view Gui::MWList* list = getWidget(mQuestMode ? QuestsList : TopicsList); - if (mSelectedQuest <= 3) - list->setViewOffset(0); - else - { - int offset = 0; - for (int i = 0; i < static_cast(mSelectedQuest) - 3; i++) - offset += mButtons[i]->getHeight() + 3; - list->setViewOffset(-offset); - } + int offset = 0; + for (int i = 3; i < static_cast(mSelectedQuest); i++) + offset += mButtons[i]->getHeight(); + list->setViewOffset(-offset); } } }; From fdc392435f60f82716a7e6e722588f544cd43de3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 6 Jul 2025 22:40:12 -0700 Subject: [PATCH 007/102] Fix controller tooltip temporarily disappearing when dealing with stacks of items --- apps/openmw/mwgui/countdialog.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 816d67921c..1f31871e33 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -71,17 +71,16 @@ namespace MWGui void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) { - eventOkClicked(nullptr, mSlider->getScrollPosition() + 1); - + // The order here matters. Hide the dialog first so the OK event tooltips reappear. setVisible(false); + eventOkClicked(nullptr, mSlider->getScrollPosition() + 1); } // essentially duplicating what the OK button does if user presses // Enter key void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) { - eventOkClicked(nullptr, mSlider->getScrollPosition() + 1); - setVisible(false); + onOkButtonClicked(_sender); // To do not spam onEnterKeyPressed() again and again MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); From 952abf55579e29c1f47f3de48125cf2cf033ce8c Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 6 Jul 2025 22:51:31 -0700 Subject: [PATCH 008/102] Support either two- or three- column layouts for cyrillic journal indices --- apps/openmw/mwgui/journalwindow.cpp | 85 ++++++++++------------------- apps/openmw/mwgui/journalwindow.hpp | 2 + 2 files changed, 31 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index e956696221..ac44264b76 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -218,6 +218,18 @@ namespace } } + // Latin = 26 (13 + 13) + mIndexRowCount = 13; + bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); + if (isRussian) + { + // This should match the logic in createCyrillicJournalIndex + if (Settings::gui().mFontSize < 18) + mIndexRowCount = 15; // Cyrillic = 30 (15 + 15) + else + mIndexRowCount = 10; // Cyrillic = 30 (10 + 10 + 10) + } + mControllerButtons.a = "#{sSelect}"; mControllerButtons.x = "#{OMWEngine:JournalQuests}"; mControllerButtons.y = "#{sTopics}"; @@ -701,21 +713,8 @@ namespace void setIndexControllerFocus(bool focused) { - int col, row; - bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); - if (isRussian) - { - // Cyrillic = 30 (10 + 10 + 10) - col = mSelectedIndex / 10; - row = mSelectedIndex % 10; - } - else - { - // Latin = 26 (13 + 13) - col = mSelectedIndex / 13; - row = mSelectedIndex % 13; - } - + int col = mSelectedIndex / mIndexRowCount; + int row = mSelectedIndex % mIndexRowCount; mTopicIndexBook->setColour(col, row, 0, focused ? MWGui::journalHeaderColour : MyGUI::Colour::Black); } @@ -831,28 +830,13 @@ namespace else if (mOptionsMode) { setIndexControllerFocus(false); - if (isRussian) + if (mSelectedIndex % mIndexRowCount == 0) { - // Cyrillic = 30 (10 + 10 + 10) - if (mSelectedIndex == 0) - mSelectedIndex = 9; - else if (mSelectedIndex == 10) - mSelectedIndex = 19; - else if (mSelectedIndex == 20) - mSelectedIndex = 29; - else - mSelectedIndex--; + int col = mSelectedIndex / mIndexRowCount; + mSelectedIndex = (col * mIndexRowCount) + mIndexRowCount - 1; } else - { - // Latin = 26 (13 + 13) - if (mSelectedIndex == 0) - mSelectedIndex = 12; - else if (mSelectedIndex == 13) - mSelectedIndex = 25; - else - mSelectedIndex--; - } + mSelectedIndex--; setIndexControllerFocus(true); setText(PageOneNum, 1); // Redraw the list } @@ -871,28 +855,13 @@ namespace else if (mOptionsMode) { setIndexControllerFocus(false); - if (isRussian) + if (mSelectedIndex % mIndexRowCount == mIndexRowCount - 1) { - // Cyrillic = 30 (10 + 10 + 10) - if (mSelectedIndex == 9) - mSelectedIndex = 0; - else if (mSelectedIndex == 19) - mSelectedIndex = 10; - else if (mSelectedIndex == 29) - mSelectedIndex = 20; - else - mSelectedIndex++; + int col = mSelectedIndex / mIndexRowCount; + mSelectedIndex = col * mIndexRowCount; } else - { - // Latin = 26 (13 + 13) - if (mSelectedIndex == 12) - mSelectedIndex = 0; - else if (mSelectedIndex == 25) - mSelectedIndex = 13; - else - mSelectedIndex++; - } + mSelectedIndex++; setIndexControllerFocus(true); setText(PageOneNum, 1); // Redraw the list } @@ -908,7 +877,11 @@ namespace if (isRussian) { // Cyrillic = 30 (10 + 10 + 10) - mSelectedIndex = (mSelectedIndex + 20) % 30; + if (mIndexRowCount == 10) + mSelectedIndex = (mSelectedIndex + 20) % 30; + // or Cyrillic = 30 (15 + 15) + else + mSelectedIndex = (mSelectedIndex + 15) % 30; } else { @@ -929,8 +902,8 @@ namespace setIndexControllerFocus(false); if (isRussian) { - // Cyrillic = 30 (10 + 10 + 10) - mSelectedIndex = (mSelectedIndex + 10) % 30; + // Cyrillic = 30 (10 + 10 + 10) or (15 + 15) + mSelectedIndex = (mSelectedIndex + mIndexRowCount) % 30; } else { diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 4a1f686ff1..ca0893fdd4 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -32,6 +32,8 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Journal"; } + size_t mIndexRowCount; + std::vector mButtons; size_t mSelectedQuest = 0; size_t mSelectedIndex = 0; From 3a05ddb982c7ad8a02e27d258cc022dc8787a5a1 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 6 Jul 2025 23:17:00 -0700 Subject: [PATCH 009/102] Fix issues caused by incomplete merge --- apps/openmw/mwgui/inventorywindow.cpp | 3 +-- apps/openmw/mwgui/tradewindow.cpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index f18e8fdcbb..0e8c67f8bd 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -90,9 +90,8 @@ namespace MWGui , mLastYSize(0) , mPreview(std::make_unique(parent, resourceSystem, MWMechanics::getPlayer())) , mTrading(false) - , mUpdateTimer(0.f) - , mPendingControllerAction(ControllerAction::None) , mUpdateNextFrame(false) + , mPendingControllerAction(ControllerAction::None) { mPreviewTexture = std::make_unique(mPreview->getTexture(), mPreview->getTextureStateSet()); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0c75779957..df7c0bda36 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -694,7 +694,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } -<<<<<<< HEAD bool TradeWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) From 21367a6127b108d06a337ef551c715677db1f52d Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 7 Jul 2025 08:58:21 -0700 Subject: [PATCH 010/102] Merge branch 'master' of https://gitlab.com/OpenMW/openmw --- apps/components_tests/lua/testl10n.cpp | 26 +++++++++++++++++++++++++ apps/openmw/mwgui/journalwindow.cpp | 2 +- apps/openmw/mwstate/statemanagerimp.cpp | 5 +++-- components/lua/l10n.cpp | 6 +++++- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/apps/components_tests/lua/testl10n.cpp b/apps/components_tests/lua/testl10n.cpp index b48028a730..5528c0520a 100644 --- a/apps/components_tests/lua/testl10n.cpp +++ b/apps/components_tests/lua/testl10n.cpp @@ -24,6 +24,8 @@ namespace constexpr VFS::Path::NormalizedView test2EnPath("l10n/test2/en.yaml"); constexpr VFS::Path::NormalizedView test3EnPath("l10n/test3/en.yaml"); constexpr VFS::Path::NormalizedView test3DePath("l10n/test3/de.yaml"); + constexpr VFS::Path::NormalizedView test4RuPath("l10n/test4/ru.yaml"); + constexpr VFS::Path::NormalizedView test4EnPath("l10n/test4/en.yaml"); VFSTestFile invalidScript("not a script"); VFSTestFile incorrectScript( @@ -69,6 +71,16 @@ currency: "You have {money, number, currency}" VFSTestFile test2En(R"X( good_morning: "Morning!" you_have_arrows: "Arrows count: {count}" +)X"); + + VFSTestFile test4Ru(R"X( +skill_increase: "Ваш навык {навык} увеличился до {value}" +acrobatics: "Акробатика" +)X"); + + VFSTestFile test4En(R"X( +stat_increase: "Your {stat} has increased to {value}" +speed: "Speed" )X"); struct LuaL10nTest : Test @@ -80,6 +92,8 @@ you_have_arrows: "Arrows count: {count}" { test2EnPath, &test2En }, { test3EnPath, &test1En }, { test3DePath, &test1De }, + { test4RuPath, &test4Ru }, + { test4EnPath, &test4En }, }); LuaUtil::ScriptsConfiguration mCfg; @@ -169,6 +183,18 @@ you_have_arrows: "Arrows count: {count}" l.safe_script("t3 = l10n('Test3', 'de')"); l10nManager.setPreferredLocales({ "en" }); EXPECT_EQ(get(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!"); + + // Test that formatting arguments use a correct encoding + l.safe_script("t4 = l10n('Test4', 'ru')"); + l10nManager.setPreferredLocales({ "ru", "en" }); + EXPECT_EQ(get(l, "t4('skill_increase', {навык='Акробатика', value=100})"), + "Ваш навык Акробатика увеличился до 100"); + EXPECT_EQ(get(l, "t4('skill_increase', {навык=t4('acrobatics'), value=100})"), + "Ваш навык Акробатика увеличился до 100"); + EXPECT_EQ(get(l, "t4('stat_increase', {stat='Speed', value=100})"), + "Your Speed has increased to 100"); + EXPECT_EQ(get(l, "t4('stat_increase', {stat=t4('speed'), value=100})"), + "Your Speed has increased to 100"); }); } } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index ac44264b76..dcc6c423dd 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -945,7 +945,7 @@ namespace // Scroll the list to keep the active item in view Gui::MWList* list = getWidget(mQuestMode ? QuestsList : TopicsList); int offset = 0; - for (int i = 3; i < static_cast(mSelectedQuest); i++) + for (int i = 4; i < static_cast(mSelectedQuest); i++) offset += mButtons[i]->getHeight(); list->setViewOffset(-offset); } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 498d2ab25a..34aa3eaa46 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -332,14 +332,15 @@ void MWState::StateManager::saveGame(std::string_view description, const Slot* s writer.close(); if (stream.fail()) - throw std::runtime_error("Write operation failed (memory stream)"); + throw std::runtime_error( + "Write operation failed (memory stream): " + std::generic_category().message(errno)); // All good, write to file std::ofstream filestream(slot->mPath, std::ios::binary); filestream << stream.rdbuf(); if (filestream.fail()) - throw std::runtime_error("Write operation failed (file stream)"); + throw std::runtime_error("Write operation failed (file stream): " + std::generic_category().message(errno)); Settings::saves().mCharacter.set(Files::pathToUnicodeString(slot->mPath.parent_path().filename())); mLastSavegame = slot->mPath; diff --git a/components/lua/l10n.cpp b/components/lua/l10n.cpp index 15177bea65..ec42992ebb 100644 --- a/components/lua/l10n.cpp +++ b/components/lua/l10n.cpp @@ -18,7 +18,11 @@ namespace { // Argument values if (value.is()) - args.push_back(icu::Formattable(LuaUtil::cast(value).c_str())); + { + const auto& str = LuaUtil::cast(value); + args.push_back(icu::Formattable(icu::UnicodeString::fromUTF8(str.c_str()))); + } + // Note: While we pass all numbers as doubles, they still seem to be handled appropriately. // Numbers can be forced to be integers using the argType number and argStyle integer // E.g. {var, number, integer} From 9a637563cd686ec0aeaa820e1776a1d5efc0f809 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 8 Jul 2025 19:46:24 -0700 Subject: [PATCH 011/102] Fix getIndexSelected typing --- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/savegamedialog.cpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 6cf34702d7..35ff668df0 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -559,7 +559,7 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_A || arg.button == SDL_CONTROLLER_BUTTON_Y) { // Select the highlighted entry in the combo box and close it. - int index = mFilterValue->getIndexSelected(); + size_t index = mFilterValue->getIndexSelected(); mFilterValue->setIndexSelected(index); onFilterChanged(mFilterValue, index); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); // Close list diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index e335601e95..b096c5c9c9 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -548,7 +548,7 @@ namespace MWGui } else if (arg.button == SDL_CONTROLLER_BUTTON_Y) { - uint32_t index = mCharacterSelection->getIndexSelected(); + size_t index = mCharacterSelection->getIndexSelected(); index = wrap(index + 1, mCharacterSelection->getItemCount()); mCharacterSelection->setIndexSelected(index); onCharacterSelected(mCharacterSelection, index); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 726809c583..406beb506f 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1142,7 +1142,7 @@ namespace MWGui } else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { - uint32_t index = mSettingsTab->getIndexSelected(); + size_t index = mSettingsTab->getIndexSelected(); index = wrap(index - 1, mSettingsTab->getItemCount()); mSettingsTab->setIndexSelected(index); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); @@ -1150,7 +1150,7 @@ namespace MWGui } else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { - uint32_t index = mSettingsTab->getIndexSelected(); + size_t index = mSettingsTab->getIndexSelected(); index = wrap(index + 1, mSettingsTab->getItemCount()); mSettingsTab->setIndexSelected(index); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); From d100867bce447d0f9677a9b842db19a614272b38 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 10 Jul 2025 08:44:58 -0700 Subject: [PATCH 012/102] Add ReST documentation for controller menu options --- .../source/reference/modding/settings/GUI.rst | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index b9065c8daf..3006ac1b56 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -62,6 +62,27 @@ GUI Settings Bethesda assets are 4:3 ratio; others may differ. If false, assets are centered with black bars filling remainder. +.. omw-setting:: + :title: controller menus + :type: boolean + :range: true, false + :default: false + :location: :bdg-success:`Launcher > Settings > Interface` + + Make menus easier to navigate with a controller. + If false, the controller works as a GUI mouse. + +.. omw-setting:: + :title: controller tooltips + :type: boolean + :range: true, false + :default: false + :location: :bdg-success:`Launcher > Settings > Interface` + + When true, you do not need to press R3 to show tooltips when using + controller menus. + If false, controller menu tooltips are hidden until R3 is pressed. + .. omw-setting:: :title: subtitles :type: boolean From e04f0a8bd682c62f5d1159a2acee33f9bec7b6ef Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 10 Jul 2025 08:45:51 -0700 Subject: [PATCH 013/102] Move logic for determining Russian index page count to a shared place --- apps/openmw/mwgui/journalbooks.cpp | 16 +++++++++------- apps/openmw/mwgui/journalbooks.hpp | 1 + apps/openmw/mwgui/journalwindow.cpp | 10 ++-------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index c127508062..c698fd84d6 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -156,6 +156,13 @@ namespace MWGui return MWGui::BookTypesetter::Utf8Span(begin, begin + text.length()); } + int getCyrillicIndexPageCount() + { + // For small font size split alphabet to two columns (2x15 characers), for big font size split it to three + // colums (3x10 characters). + return Settings::gui().mFontSize < 18 ? 2 : 3; + } + typedef TypesetBook::Ptr book; JournalBooks::JournalBooks(JournalViewModel::Ptr model, ToUTF8::FromType encoding) @@ -277,13 +284,8 @@ namespace MWGui // for small font size split alphabet to two columns (2x15 characers), for big font size split it to three // colums (3x10 characters). - int sectionBreak = 10; - mIndexPagesCount = 3; - if (Settings::gui().mFontSize < 18) - { - sectionBreak = 15; - mIndexPagesCount = 2; - } + mIndexPagesCount = getCyrillicIndexPageCount(); + int sectionBreak = 30 / mIndexPagesCount; unsigned char ch[3] = { 0xd0, 0x90, 0x00 }; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8 diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index 2549b2ad5b..3d55135d9c 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -9,6 +9,7 @@ namespace MWGui { MWGui::BookTypesetter::Utf8Span to_utf8_span(std::string_view text); + int getCyrillicIndexPageCount(); const MyGUI::Colour journalHeaderColour = MyGUI::Colour(0.60f, 0.00f, 0.00f); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index dcc6c423dd..a462ed914d 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -221,14 +221,8 @@ namespace // Latin = 26 (13 + 13) mIndexRowCount = 13; bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); - if (isRussian) - { - // This should match the logic in createCyrillicJournalIndex - if (Settings::gui().mFontSize < 18) - mIndexRowCount = 15; // Cyrillic = 30 (15 + 15) - else - mIndexRowCount = 10; // Cyrillic = 30 (10 + 10 + 10) - } + if (isRussian) // Cyrillic is either (10 + 10 + 10) or (15 + 15) + mIndexRowCount = MWGui::getCyrillicIndexPageCount(); mControllerButtons.a = "#{sSelect}"; mControllerButtons.x = "#{OMWEngine:JournalQuests}"; From b0754285136e481a26840b5709665a0b5207ffd5 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 10 Jul 2025 08:46:01 -0700 Subject: [PATCH 014/102] Minor fixes based on code review feedback. --- .../openmw/mwgui/controllerbuttonsoverlay.cpp | 22 +++++++++---------- apps/openmw/mwgui/dialogue.cpp | 11 ++++++---- apps/openmw/mwgui/dialogue.hpp | 3 --- apps/openmw/mwgui/inventorywindow.cpp | 10 ++++----- apps/openmw/mwgui/windowbase.cpp | 2 +- apps/openmw/mwgui/windowbase.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +---- apps/openmw/mwinput/controllermanager.cpp | 2 +- 8 files changed, 27 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index 065fb46d77..aee2ae2631 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -99,14 +99,23 @@ namespace MWGui void ControllerButtonsOverlay::setIcon(MyGUI::ImageBox* image, const std::string& imagePath) { - if (imagePath.length() > 0) + if (!imagePath.empty()) image->setImageTexture(imagePath); } int ControllerButtonsOverlay::updateButton( MyGUI::TextBox* text, MyGUI::ImageBox* image, const std::string& buttonStr) { - if (buttonStr.length() > 0) + if (buttonStr.empty()) + { + image->setVisible(false); + image->setUserString("Hidden", "true"); + + text->setVisible(false); + text->setUserString("Hidden", "true"); + return 0; + } + else { image->setVisible(true); image->setUserString("Hidden", "false"); @@ -117,14 +126,5 @@ namespace MWGui text->setSize(text->getTextSize().width + 16, 48); return 1; } - else - { - image->setVisible(false); - image->setUserString("Hidden", "true"); - - text->setVisible(false); - text->setUserString("Hidden", "true"); - return 0; - } } } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 0d694a6c4d..20fd6c9e3c 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -347,6 +347,10 @@ namespace MWGui // -------------------------------------------------------------------------------------------------- + + // Morrowind uses 3 px invisible borders for padding topics + static constexpr int sVerticalPadding = 3; + DialogueWindow::DialogueWindow() : WindowBase("openmw_dialogue_window.layout") , mIsCompanion(false) @@ -603,10 +607,9 @@ namespace MWGui void DialogueWindow::updateTopicsPane() { - const std::string focusedTopic - = Settings::gui().mControllerMenus && mControllerFocus < static_cast(mTopicsList->getItemCount()) - ? mTopicsList->getItemNameAt(mControllerFocus) - : ""; + std::string focusedTopic; + if (Settings::gui().mControllerMenus && mControllerFocus < static_cast(mTopicsList->getItemCount())) + focusedTopic = mTopicsList->getItemNameAt(mControllerFocus); mTopicsList->clear(); for (auto& linkPair : mTopicLinks) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 90f1c3eaaf..6f03076e92 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -226,9 +226,6 @@ namespace MWGui MyGUI::IntSize mCurrentWindowSize; - // Morrowind uses 3 px invisible borders for padding topics - static const int sVerticalPadding = 3; - std::unique_ptr mCallback; std::unique_ptr mGreetingCallback; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0e8c67f8bd..642cfed05d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -977,22 +977,22 @@ namespace MWGui case MWGui::GM_Companion: mControllerButtons.a = "#{OMWEngine:InventorySelect}"; mControllerButtons.b = "#{sClose}"; - mControllerButtons.x = ""; - mControllerButtons.y = ""; + mControllerButtons.x.clear(); + mControllerButtons.y.clear(); mControllerButtons.r2 = "#{sCompanionShare}"; break; case MWGui::GM_Container: mControllerButtons.a = "#{OMWEngine:InventorySelect}"; mControllerButtons.b = "#{sClose}"; mControllerButtons.x = "#{sTakeAll}"; - mControllerButtons.y = ""; + mControllerButtons.y.clear(); mControllerButtons.r2 = "#{sContainer}"; break; case MWGui::GM_Barter: mControllerButtons.a = "#{sSell}"; mControllerButtons.b = "#{sCancel}"; mControllerButtons.x = "#{sOffer}"; - mControllerButtons.y = ""; + mControllerButtons.y.clear(); mControllerButtons.r2 = "#{sBarter}"; break; case MWGui::GM_Inventory: @@ -1001,7 +1001,7 @@ namespace MWGui mControllerButtons.b = "#{sBack}"; mControllerButtons.x = "#{sDrop}"; mControllerButtons.y = "#{sUnequip}"; - mControllerButtons.r2 = ""; + mControllerButtons.r2.clear(); break; } return &mControllerButtons; diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index b607ec50b2..d6a5cb9086 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -25,7 +25,7 @@ int MWGui::wrap(int index, int max) return index; } -void MWGui::setControllerFocus(std::vector buttons, int index, bool focused) +void MWGui::setControllerFocus(const std::vector& buttons, int index, bool focused) { if (index >= 0 && index < static_cast(buttons.size())) buttons[index]->setStateSelected(focused); diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index b4d17a1ba3..fb88af499a 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -15,7 +15,7 @@ namespace MWGui class DragAndDrop; int wrap(int index, int max); - void setControllerFocus(std::vector buttons, int index, bool selected); + void setControllerFocus(const std::vector& buttons, int index, bool selected); struct ControllerButtonStr { diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 145e07d2c7..866dd1580a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -898,9 +898,6 @@ namespace MWGui int activeIndex = std::clamp(mActiveControllerWindows[mode], 0, (int)state.mWindows.size() - 1); - Log(Debug::Debug) << "Getting active controller window: mode=" << mode << ", " << state.mWindows.size() - << " window(s), activeIndex=" << activeIndex; - // If the active window is no longer visible, find the next visible window. if (!state.mWindows[activeIndex]->isVisible()) cycleActiveControllerWindow(true); @@ -2634,7 +2631,7 @@ namespace MWGui if (!Settings::gui().mControllerMenus || !mControllerButtonsOverlay) return; - WindowBase* topWin = this->getActiveControllerWindow(); + WindowBase* topWin = getActiveControllerWindow(); if (!topWin || !topWin->isVisible()) { mControllerButtonsOverlay->setVisible(false); diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 68cd9a7dbd..61e12a3b1a 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -538,7 +538,7 @@ namespace MWInput return "textures/omw_steam_button_y.dds"; case SDL_CONTROLLER_BUTTON_GUIDE: default: - return ""; + return {}; } } From 73ffe7f62b2a96c50c6e4eb82bbbac106e5a2ad4 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 10 Jul 2025 08:56:24 -0700 Subject: [PATCH 015/102] Fix clang formatting issue --- apps/openmw/mwgui/dialogue.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 20fd6c9e3c..49ceafbfd8 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -347,7 +347,6 @@ namespace MWGui // -------------------------------------------------------------------------------------------------- - // Morrowind uses 3 px invisible borders for padding topics static constexpr int sVerticalPadding = 3; From 622cbd528874be3c739783d62365489ef2522f85 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 12 Jul 2025 14:17:23 -0700 Subject: [PATCH 016/102] Remove SDL_VERSION_ATLEAST references --- apps/openmw/mwinput/controllermanager.cpp | 30 ++++++----------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 61e12a3b1a..dbda192103 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -467,29 +467,20 @@ namespace MWInput int ControllerManager::getControllerType() { - int type = 0; -#if SDL_VERSION_ATLEAST(2, 0, 12) SDL_GameController* cntrl = mBindingsManager->getControllerOrNull(); if (cntrl) - type = SDL_GameControllerGetType(cntrl); -#endif - return type; + return SDL_GameControllerGetType(cntrl); + return 0; } std::string ControllerManager::getControllerButtonIcon(int button) { int controllerType = ControllerManager::getControllerType(); - bool isXbox = false; - bool isPsx = false; - bool isSwitch = false; - -#if SDL_VERSION_ATLEAST(2, 0, 12) - isXbox = controllerType == SDL_CONTROLLER_TYPE_XBOX360 || controllerType == SDL_CONTROLLER_TYPE_XBOXONE; - isPsx = controllerType == SDL_CONTROLLER_TYPE_PS3 || controllerType == SDL_CONTROLLER_TYPE_PS4 - || controllerType == SDL_CONTROLLER_TYPE_PS5; - isSwitch = controllerType == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; -#endif + bool isXbox = controllerType == SDL_CONTROLLER_TYPE_XBOX360 || controllerType == SDL_CONTROLLER_TYPE_XBOXONE; + bool isPsx = controllerType == SDL_CONTROLLER_TYPE_PS3 || controllerType == SDL_CONTROLLER_TYPE_PS4 + || controllerType == SDL_CONTROLLER_TYPE_PS5;; + bool isSwitch = controllerType == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; switch (button) { @@ -546,13 +537,8 @@ namespace MWInput { int controllerType = ControllerManager::getControllerType(); - bool isXbox = false; - bool isSwitch = false; - -#if SDL_VERSION_ATLEAST(2, 0, 12) - isXbox = controllerType == SDL_CONTROLLER_TYPE_XBOX360 || controllerType == SDL_CONTROLLER_TYPE_XBOXONE; - isSwitch = controllerType == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; -#endif + bool isXbox = controllerType == SDL_CONTROLLER_TYPE_XBOX360 || controllerType == SDL_CONTROLLER_TYPE_XBOXONE; + bool isSwitch = controllerType == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; switch (axis) { From 084e4a3155bb3cb56306689d3e24f01896784cc3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 12 Jul 2025 15:13:33 -0700 Subject: [PATCH 017/102] Change type of mControllerFocus to size_t when applicable --- apps/openmw/mwgui/class.cpp | 10 ++++++---- apps/openmw/mwgui/class.hpp | 8 ++++---- apps/openmw/mwgui/levelupdialog.cpp | 2 +- apps/openmw/mwgui/levelupdialog.hpp | 2 +- apps/openmw/mwgui/merchantrepair.cpp | 4 ++-- apps/openmw/mwgui/merchantrepair.hpp | 2 +- apps/openmw/mwgui/messagebox.cpp | 19 ++++++++++++------- apps/openmw/mwgui/messagebox.hpp | 6 +++--- apps/openmw/mwgui/quickkeysmenu.cpp | 7 ++++--- apps/openmw/mwgui/quickkeysmenu.hpp | 2 +- apps/openmw/mwgui/review.cpp | 1 + apps/openmw/mwgui/review.hpp | 2 +- apps/openmw/mwgui/spellbuyingwindow.cpp | 5 +++-- apps/openmw/mwgui/spellbuyingwindow.hpp | 2 +- apps/openmw/mwgui/trainingwindow.cpp | 2 +- apps/openmw/mwgui/trainingwindow.hpp | 2 +- apps/openmw/mwgui/travelwindow.cpp | 2 +- apps/openmw/mwgui/travelwindow.hpp | 2 +- 18 files changed, 45 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 450a61b4f8..121793c3af 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -383,6 +383,7 @@ namespace MWGui InfoBoxDialog::InfoBoxDialog() : WindowModal("openmw_infobox.layout") + , mControllerFocus(0) { getWidget(mTextBox, "TextBox"); getWidget(mText, "Text"); @@ -467,7 +468,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) + if (mControllerFocus < mButtons.size()) onButtonClicked(mButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -490,7 +491,7 @@ namespace MWGui { if (mButtons.size() <= 1) return true; - if (mButtons.size() == 2 && mControllerFocus == static_cast(mButtons.size()) - 1) + if (mButtons.size() == 2 && mControllerFocus == 1) return true; setControllerFocus(mButtons, mControllerFocus, false); @@ -524,6 +525,7 @@ namespace MWGui : WindowModal("openmw_chargen_create_class.layout") , mAffectedAttribute(nullptr) , mAffectedSkill(nullptr) + , mControllerFocus(2) { // Centre dialog center(); @@ -992,7 +994,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mAttributeButtons.size())) + if (mControllerFocus < mAttributeButtons.size()) onAttributeClicked(mAttributeButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -1094,7 +1096,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mSkillButtons.size())) + if (mControllerFocus < mSkillButtons.size()) onSkillClicked(mSkillButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index a4db03d4ab..2f394af9c0 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -51,7 +51,7 @@ namespace MWGui MyGUI::TextBox* mText; MyGUI::Widget* mButtonBar; std::vector mButtons; - int mControllerFocus = 0; + size_t mControllerFocus; }; // Lets the player choose between 3 ways of creating a class @@ -218,7 +218,7 @@ namespace MWGui void onAttributeClicked(Widgets::MWAttributePtr _sender); void onCancelClicked(MyGUI::Widget* _sender); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - int mControllerFocus; + size_t mControllerFocus; std::vector mAttributeButtons; private: @@ -252,7 +252,7 @@ namespace MWGui void onSkillClicked(Widgets::MWSkillPtr _sender); void onCancelClicked(MyGUI::Widget* _sender); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - int mControllerFocus; + size_t mControllerFocus; std::vector mSkillButtons; private: @@ -350,7 +350,7 @@ namespace MWGui Widgets::MWSkillPtr mAffectedSkill; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - int mControllerFocus = 2; + size_t mControllerFocus; }; } #endif diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index b9c90e51e2..34f70e8660 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -386,7 +386,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mAttributeButtons.size())) + if (mControllerFocus < mAttributeButtons.size()) onAttributeClicked(mAttributeButtons[mControllerFocus]); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Item Gold Up")); } diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp index 8a34a94c8b..05048b3e8f 100644 --- a/apps/openmw/mwgui/levelupdialog.hpp +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -52,7 +52,7 @@ namespace MWGui bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; std::vector mAttributeButtons; - int mControllerFocus; + size_t mControllerFocus; }; } diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 425c519175..db0f3bf1a2 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -178,7 +178,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) + if (mControllerFocus < mButtons.size()) onRepairButtonClick(mButtons[mControllerFocus].first); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -205,7 +205,7 @@ namespace MWGui } // Scroll the list to keep the active item in view - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) + if (mControllerFocus < mButtons.size()) { int line = mButtons[mControllerFocus].second; if (line <= 5) diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp index e878d34b7d..8bc9ba4bac 100644 --- a/apps/openmw/mwgui/merchantrepair.hpp +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -27,7 +27,7 @@ namespace MWGui MWWorld::Ptr mActor; - int mControllerFocus; + size_t mControllerFocus; protected: void onMouseWheel(MyGUI::Widget* _sender, int _rel); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 80b2a34a5b..8020a4c3d5 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -218,7 +218,7 @@ namespace MWGui } InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, - const std::vector& buttons, bool immediate, int defaultFocus) + const std::vector& buttons, bool immediate, size_t defaultFocus) : WindowModal(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "openmw_interactive_messagebox_notransp.layout" : "openmw_interactive_messagebox.layout") @@ -226,6 +226,7 @@ namespace MWGui , mButtonPressed(-1) , mDefaultFocus(defaultFocus) , mImmediate(immediate) + , mControllerFocus(0) { int textPadding = 10; // padding between text-widget and main-widget int textButtonPadding = 10; // padding between the text-widget und the button-widget @@ -290,9 +291,9 @@ namespace MWGui if (mButtons.size() > 1) { mControllerFocus = 0; - if (mDefaultFocus >= 0 && mDefaultFocus < static_cast(mButtons.size())) + if (mDefaultFocus < mButtons.size()) mControllerFocus = mDefaultFocus; - for (int i = 0; i < static_cast(mButtons.size()); ++i) + for (size_t i = 0; i < mButtons.size(); ++i) mButtons[i]->setStateSelected(i == mControllerFocus); } } @@ -397,7 +398,7 @@ namespace MWGui MyGUI::Widget* InteractiveMessageBox::getDefaultKeyFocus() { - if (mDefaultFocus >= 0 && mDefaultFocus < static_cast(mButtons.size())) + if (mDefaultFocus < mButtons.size()) return mButtons[mDefaultFocus]; auto& languageManager = MyGUI::LanguageManager::getInstance(); std::vector keywords{ languageManager.replaceTags("#{sOk}"), @@ -452,8 +453,12 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - mControllerFocus = std::clamp(mControllerFocus, 0, static_cast(mButtons.size()) - 1); - buttonActivated(mButtons[mControllerFocus]); + if (!mButtons.empty()) + { + if (mControllerFocus >= mButtons.size()) + mControllerFocus = mButtons.size() - 1; + buttonActivated(mButtons[mControllerFocus]); + } } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { @@ -475,7 +480,7 @@ namespace MWGui { if (mButtons.size() <= 1) return true; - if (mButtons.size() == 2 && mControllerFocus == static_cast(mButtons.size()) - 1) + if (mButtons.size() == 2 && mControllerFocus == 1) return true; setControllerFocus(mButtons, mControllerFocus, false); diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index e6128ee0d1..98eb69cc69 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -93,7 +93,7 @@ namespace MWGui { public: InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, - const std::vector& buttons, bool immediate, int defaultFocus); + const std::vector& buttons, bool immediate, size_t defaultFocus); void mousePressed(MyGUI::Widget* _widget); int readPressedButton(); @@ -114,9 +114,9 @@ namespace MWGui std::vector mButtons; int mButtonPressed; - int mDefaultFocus; + size_t mDefaultFocus; bool mImmediate; - int mControllerFocus = 0; + size_t mControllerFocus; }; } diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 1b25b8b7d2..419cb26420 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -39,7 +39,7 @@ namespace MWGui , mKey(std::vector(10)) , mSelected(nullptr) , mActivated(nullptr) - + , mControllerFocus(0) { getWidget(mOkButton, "OKButton"); getWidget(mInstructionLabel, "InstructionLabel"); @@ -118,7 +118,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerFocus = 0; - for (int i = 0; i < static_cast(mKey.size()); i++) + for (size_t i = 0; i < mKey.size(); i++) mKey[i].button->setControllerFocus(i == mControllerFocus); } } @@ -490,7 +490,7 @@ namespace MWGui mControllerFocus++; } - for (int i = 0; i < static_cast(mKey.size()); i++) + for (size_t i = 0; i < mKey.size(); i++) mKey[i].button->setControllerFocus(i == mControllerFocus); return true; @@ -501,6 +501,7 @@ namespace MWGui QuickKeysMenuAssign::QuickKeysMenuAssign(QuickKeysMenu* parent) : WindowModal("openmw_quickkeys_menu_assign.layout") , mParent(parent) + , mControllerFocus(0) { getWidget(mLabel, "Label"); getWidget(mItemButton, "ItemButton"); diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 228eb926b4..e740a93a68 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -74,7 +74,7 @@ namespace MWGui void unassign(keyData* key); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - int mControllerFocus; + size_t mControllerFocus; }; class QuickKeysMenuAssign : public WindowModal diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index dcaa49b1e2..236c7198d2 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -36,6 +36,7 @@ namespace MWGui ReviewDialog::ReviewDialog() : WindowModal("openmw_chargen_review.layout") , mUpdateSkillArea(false) + , mControllerFocus(5) { // Centre dialog center(); diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index fe53787fe3..cd3fc594a7 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -104,7 +104,7 @@ namespace MWGui // 0 = Name, 1 = Race, 2 = Class, 3 = BirthSign, 4 = Back, 5 = OK std::vector mButtons; - int mControllerFocus = 5; + int mControllerFocus; }; } #endif diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index e43bbfc497..4b6811e605 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -27,6 +27,7 @@ namespace MWGui SpellBuyingWindow::SpellBuyingWindow() : WindowBase("openmw_spell_buying_window.layout") , mCurrentY(0) + , mControllerFocus(0) { getWidget(mCancelButton, "CancelButton"); getWidget(mPlayerGold, "PlayerGold"); @@ -229,7 +230,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mSpellButtons.size())) + if (mControllerFocus < mSpellButtons.size()) onSpellButtonClick(mSpellButtons[mControllerFocus].first); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -261,7 +262,7 @@ namespace MWGui mSpellButtons[mControllerFocus].first->setStateSelected(true); } - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mSpellButtons.size())) + if (mControllerFocus < mSpellButtons.size()) { // Scroll the list to keep the active item in view int line = mSpellButtons[mControllerFocus].second; diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index e67dfde76c..7a75afbed1 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -58,7 +58,7 @@ namespace MWGui private: static bool sortSpells(const ESM::Spell* left, const ESM::Spell* right); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - int mControllerFocus; + size_t mControllerFocus; }; } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 1a268fff5d..be8359cde5 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -250,7 +250,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mTrainingButtons.size())) + if (mControllerFocus < mTrainingButtons.size()) onTrainingSelected(mTrainingButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) diff --git a/apps/openmw/mwgui/trainingwindow.hpp b/apps/openmw/mwgui/trainingwindow.hpp index 4f866f820e..f44c5524f5 100644 --- a/apps/openmw/mwgui/trainingwindow.hpp +++ b/apps/openmw/mwgui/trainingwindow.hpp @@ -55,7 +55,7 @@ namespace MWGui TimeAdvancer mTimeAdvancer; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - int mControllerFocus; + size_t mControllerFocus; }; } diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 726e623eb3..38eee1b85c 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -263,7 +263,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mDestinationButtons.size())) + if (mControllerFocus < mDestinationButtons.size()) { onTravelButtonClick(mDestinationButtons[mControllerFocus]); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 79f5f9abc8..c0002b805b 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -42,7 +42,7 @@ namespace MWGui private: bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - int mControllerFocus; + size_t mControllerFocus; }; } From 6629a186b1f1f025b73899d5b86490963303aac1 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 12 Jul 2025 15:37:48 -0700 Subject: [PATCH 018/102] Controller mode uses LB/RB in journal and spell list to scroll faster --- apps/openmw/mwgui/journalwindow.cpp | 12 ++++++++++-- apps/openmw/mwgui/spellview.cpp | 7 ++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index a462ed914d..ac8e779505 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -911,13 +911,21 @@ namespace } else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) // LB: Previous Page { - if (!mOptionsMode) + // Scroll through the list of quests or topics + if (mOptionsMode && (mQuestMode || mTopicsMode)) + setControllerFocusedQuest(std::max(int(mSelectedQuest) - 5, 0)); + // Page through the journal + else if (!mOptionsMode) notifyPrevPage(getWidget(PrevPageBTN)); return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) // RB: Next Page { - if (!mOptionsMode) + // Scroll through the list of quests or topics + if (mOptionsMode && (mQuestMode || mTopicsMode)) + setControllerFocusedQuest(std::min(mSelectedQuest + 5, mButtons.size() - 1)); + // Page through the journal + else if (!mOptionsMode) notifyNextPage(getWidget(NextPageBTN)); return true; } diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index a3eef23092..44bdcffe07 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -389,14 +389,19 @@ namespace MWGui else if (button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { // Jump to first item in next group + int newFocus = mControllerFocus; for (int groupIndex : mGroupIndices) { if (groupIndex > mControllerFocus) { - mControllerFocus = groupIndex; + newFocus = groupIndex; break; } } + // If on last group, jump to bottom of whole list + if (newFocus == mControllerFocus) + newFocus = mButtons.size() - 1; + mControllerFocus = newFocus; } mControllerFocus = wrap(mControllerFocus, mButtons.size()); From 843a6487cc91b2fcee6879eb3a2d48e5ca6d0f1d Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 12 Jul 2025 19:04:14 -0700 Subject: [PATCH 019/102] Fix clang warning --- apps/openmw/mwinput/controllermanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index dbda192103..6c5e81f065 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -479,7 +479,7 @@ namespace MWInput bool isXbox = controllerType == SDL_CONTROLLER_TYPE_XBOX360 || controllerType == SDL_CONTROLLER_TYPE_XBOXONE; bool isPsx = controllerType == SDL_CONTROLLER_TYPE_PS3 || controllerType == SDL_CONTROLLER_TYPE_PS4 - || controllerType == SDL_CONTROLLER_TYPE_PS5;; + || controllerType == SDL_CONTROLLER_TYPE_PS5; bool isSwitch = controllerType == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; switch (button) From 42109fc811fa03c9a037af0ee309e458ce06f149 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 13 Jul 2025 05:10:14 -0700 Subject: [PATCH 020/102] CLEANUP: Remove dead code in cloneRecordImp --- apps/opencs/model/world/collection.hpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index fa42ee0f09..18fb73330c 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -273,15 +273,6 @@ namespace CSMWorld copy->mState = RecordBase::State_ModifiedOnly; setRecordId(destination, copy->get()); - if constexpr (std::is_same_v) - { - if (type == UniversalId::Type_Reference) - { - CSMWorld::CellRef* ptr = (CSMWorld::CellRef*)©->mModified; - ptr->mRefNum.mIndex = 0; - } - } - if constexpr (std::is_same_v) { copy->mModified.mStringId = copy->mModified.mId.getRefIdString(); From 85fdf2011acaebbc1e5a4184de57b60b3b7b068a Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 13 Jul 2025 05:10:43 -0700 Subject: [PATCH 021/102] FIX: Do not attempt to bump refNums during savingStages --- apps/opencs/model/doc/savingstages.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 12fa6e811f..edd4329f7f 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -316,13 +316,7 @@ void CSMDoc::WriteCellCollectionStage::writeReferences( } ESM::RefId streamId = ESM::RefId::stringRefId(stream.str()); - if (refRecord.mNew || refRecord.mRefNum.mIndex == 0 - || (!interior && ref.mState == CSMWorld::RecordBase::State_ModifiedOnly && refRecord.mCell != streamId)) - { - refRecord.mRefNum.mIndex = newRefNum++; - } - else if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) != streamId - && !interior) + if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) != streamId && !interior) { // An empty mOriginalCell is meant to indicate that it is the same as // the current cell. It is possible that a moved ref is moved again. From 2cb7d6c3924c022cb88b820de028f87bb5218a26 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 13 Jul 2025 05:11:38 -0700 Subject: [PATCH 022/102] CLEANUP: Remove commented code & use the builtin getNewId function when cloning --- apps/opencs/view/render/instancemode.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 03872a3d6c..d48e7abcb1 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -1243,9 +1243,8 @@ void CSVRender::InstanceMode::cloneSelectedInstances() if (CSVRender::ObjectTag* objectTag = dynamic_cast(tag.get())) { macro.push(new CSMWorld::CloneCommand(referencesTable, objectTag->mObject->getReferenceId(), - "ref#" + std::to_string(referencesTable.rowCount()), CSMWorld::UniversalId::Type_Reference)); + document.getData().getReferences().getNewId(), CSMWorld::UniversalId::Type_Reference)); } - // getWorldspaceWidget().clearSelection(Mask_Reference); } void CSVRender::InstanceMode::dropInstance(CSVRender::Object* object, float dropHeight) From 79d86bf2bdc4964f67d0103f53af5b55f8083c14 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 13 Jul 2025 05:12:42 -0700 Subject: [PATCH 023/102] FIX: Track the highest local refNum during plugin loading and increment it for each cloned/created reference --- apps/opencs/model/world/refcollection.cpp | 13 ++++++++++--- apps/opencs/model/world/refcollection.hpp | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 124e697de8..5d2ed2cfc5 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -175,6 +175,9 @@ void CSMWorld::RefCollection::load(ESM::ESMReader& reader, int cellIndex, bool b ref.mIdNum = mNextId; // FIXME: fragile ref.mId = ESM::RefId::stringRefId(getNewId()); + if (!base && ref.mRefNum.mIndex >= mHighestUsedRefNum) + mHighestUsedRefNum = ref.mRefNum.mIndex; + cache.emplace(ref.mRefNum, ref.mIdNum); auto record = std::make_unique>(); @@ -222,6 +225,11 @@ std::string CSMWorld::RefCollection::getNewId() return "ref#" + std::to_string(mNextId++); } +uint32_t CSMWorld::RefCollection::getNextRefNum() +{ + return ++mHighestUsedRefNum; +} + unsigned int CSMWorld::RefCollection::extractIdNum(std::string_view id) const { std::string::size_type separator = id.find_last_of('#'); @@ -283,6 +291,7 @@ void CSMWorld::RefCollection::appendBlankRecord(const ESM::RefId& id, UniversalI record->get().mId = id; record->get().mIdNum = extractIdNum(id.getRefIdString()); + record->get().mRefNum.mIndex = getNextRefNum(); Collection::appendRecord(std::move(record)); } @@ -298,15 +307,13 @@ void CSMWorld::RefCollection::cloneRecord( copy->get().mId = destination; copy->get().mIdNum = extractIdNum(destination.getRefIdString()); + copy->get().mRefNum.mIndex = getNextRefNum(); if (copy->get().mRefNum.hasContentFile()) { mRefIndex.insert(std::make_pair(static_cast*>(copy.get())->get().mIdNum, index)); copy->get().mRefNum.mContentFile = -1; - copy->get().mRefNum.mIndex = index; } - else - copy->get().mRefNum.mIndex = copy->get().mIdNum; insertRecord(std::move(copy), getAppendIndex(destination, type)); // call RefCollection::insertRecord() } diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index d3d200e6c2..f70167f922 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -1,6 +1,7 @@ #ifndef CSM_WOLRD_REFCOLLECTION_H #define CSM_WOLRD_REFCOLLECTION_H +#include #include #include #include @@ -40,9 +41,12 @@ namespace CSMWorld std::map mRefIndex; // CellRef index keyed by CSMWorld::CellRef::mIdNum int mNextId; + uint32_t mHighestUsedRefNum = 0; unsigned int extractIdNum(std::string_view id) const; + uint32_t getNextRefNum(); + int getIntIndex(unsigned int id) const; int searchId(unsigned int id) const; From a894481fd78cb6795236fa65adda3de2f4884879 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 13 Jul 2025 15:12:33 -0700 Subject: [PATCH 024/102] Clean up controller logic to match item transfer logic --- apps/openmw/mwgui/inventorywindow.cpp | 11 +++++++++-- apps/openmw/mwgui/inventorywindow.hpp | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 85a7bd7da3..4aada181fa 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -336,7 +336,8 @@ namespace MWGui // Show a dialog to select a count of items, but not when using an item from the inventory // in controller mode. In that case, we skip the dialog and just use one item immediately. - if (count > 1 && !shift && mPendingControllerAction != ControllerAction::Use) + if (count > 1 && !shift && mPendingControllerAction != ControllerAction::Use + && mPendingControllerAction != ControllerAction::Unequip) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); std::string message = "#{sTake}"; @@ -373,6 +374,12 @@ namespace MWGui // stack of items; we only want to use the first item. onBackgroundSelected(); } + else if (mPendingControllerAction == ControllerAction::Unequip) + { + // Drop on inventory background to unequip + dragItem(nullptr, count); + onBackgroundSelected(); + } else if (mPendingControllerAction == ControllerAction::Drop) dropItem(nullptr, count); else if (mTrading || mPendingControllerAction == ControllerAction::Sell) @@ -1059,8 +1066,8 @@ namespace MWGui if (mGuiMode == MWGui::GM_Inventory) { // Unequip an item. + mPendingControllerAction = ControllerAction::Unequip; mItemView->onControllerButton(SDL_CONTROLLER_BUTTON_A); - onBackgroundSelected(); // Drop on inventory background to unequip } } else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 7be7155d03..d2272ec775 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -138,6 +138,7 @@ namespace MWGui Transfer, Sell, Drop, + Unequip, }; ControllerAction mPendingControllerAction; From 1b240c7dd655bab49cd1334a0b9257d4d0889946 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 13 Jul 2025 15:24:16 -0700 Subject: [PATCH 025/102] Clean up controller logic to match item transfer logic --- apps/openmw/mwgui/inventorywindow.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 4aada181fa..0a5715334a 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -349,10 +349,10 @@ namespace MWGui name += MWGui::ToolTips::getSoulString(object.getCellRef()); dialog->openCountDialog(name, message, count); dialog->eventOkClicked.clear(); - if (mPendingControllerAction == ControllerAction::Drop) - dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dropItem); - else if (mTrading || mPendingControllerAction == ControllerAction::Sell) + if (mTrading || mPendingControllerAction == ControllerAction::Sell) dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem); + else if (mPendingControllerAction == ControllerAction::Drop) + dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dropItem); else if (MyGUI::InputManager::getInstance().isAltPressed() || mPendingControllerAction == ControllerAction::Transfer) dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::transferItem); @@ -365,7 +365,9 @@ namespace MWGui { mSelectedItem = index; - if (mPendingControllerAction == ControllerAction::Use) + if (mTrading || mPendingControllerAction == ControllerAction::Sell) + sellItem(nullptr, count); + else if (mPendingControllerAction == ControllerAction::Use) { // Drag and drop the item on the avatar to activate it. dragItem(nullptr, count); @@ -382,8 +384,6 @@ namespace MWGui } else if (mPendingControllerAction == ControllerAction::Drop) dropItem(nullptr, count); - else if (mTrading || mPendingControllerAction == ControllerAction::Sell) - sellItem(nullptr, count); else if (MyGUI::InputManager::getInstance().isAltPressed() || mPendingControllerAction == ControllerAction::Transfer) transferItem(nullptr, count); From a296fa2b40724207dcd6a67dbc0fdfc9b65f3316 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 14 Jul 2025 17:24:28 -0700 Subject: [PATCH 026/102] Add more details to "controller menus" setting. --- docs/source/reference/modding/settings/GUI.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index 0e6b8b42b8..51e87b32ef 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -69,7 +69,16 @@ GUI Settings :default: false :location: :bdg-success:`Launcher > Settings > Interface` - Make menus easier to navigate with a controller. + Menus are now fully navigable with a controller using the DPad, triggers, and + face buttons. Most button mappings follow the Xbox version, with modern + additions like R3 to toggle item info. All menus can be opened and closed + with the controller, though some elements—such as editing settings or viewing + certain tooltips—still use the controller’s mouse cursor. + A button hint bar appears at the bottom of each menu, with icons that match + the connected controller type (Xbox, PlayStation, Switch, or Steam). In the + inventory menu, L2/R2 switch between sub-menus (Map, Inventory, Magic, Stats), + while L1/R1 adjust filters in inventory and barter screens. R3 toggles + tooltips, similar to Oblivion Remastered. Mouse input remains fully supported. If false, the controller works as a GUI mouse. .. omw-setting:: From b8381f6e626a26a712b75356530f9bed03014335 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 14 Jul 2025 17:25:57 -0700 Subject: [PATCH 027/102] Replace C-style cast with C++ cast. --- apps/openmw/mwgui/journalwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index ac8e779505..9b7b8096e5 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -913,7 +913,7 @@ namespace { // Scroll through the list of quests or topics if (mOptionsMode && (mQuestMode || mTopicsMode)) - setControllerFocusedQuest(std::max(int(mSelectedQuest) - 5, 0)); + setControllerFocusedQuest(std::max(static_cast(mSelectedQuest) - 5, 0)); // Page through the journal else if (!mOptionsMode) notifyPrevPage(getWidget(PrevPageBTN)); From af6634ee577f373a2783824f2ac3dbb510af4489 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 14 Jul 2025 17:27:20 -0700 Subject: [PATCH 028/102] Remove spurious debug log line --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 20adc9924c..ee9df120c8 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -934,8 +934,6 @@ namespace MWGui } } - Log(Debug::Debug) << "Cycling active controller window: mode=" << mode << ", activeIndex=" << activeIndex; - if (mActiveControllerWindows[mode] != activeIndex) setActiveControllerWindow(mode, activeIndex); } From 9e2927f7340e1f13e63786571cfe4d6ba959e181 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 14 Jul 2025 17:45:03 -0700 Subject: [PATCH 029/102] Use the localized strings from Interface l10n module where possible --- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/bookwindow.cpp | 2 +- apps/openmw/mwgui/class.cpp | 8 ++++---- apps/openmw/mwgui/companionwindow.cpp | 2 +- apps/openmw/mwgui/confirmationdialog.cpp | 4 ++-- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/countdialog.cpp | 4 ++-- apps/openmw/mwgui/dialogue.cpp | 2 +- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 6 +++--- apps/openmw/mwgui/itemselection.cpp | 2 +- apps/openmw/mwgui/journalwindow.cpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 4 ++-- apps/openmw/mwgui/merchantrepair.cpp | 2 +- apps/openmw/mwgui/messagebox.cpp | 2 +- apps/openmw/mwgui/quickkeysmenu.cpp | 6 +++--- apps/openmw/mwgui/recharge.cpp | 2 +- apps/openmw/mwgui/repair.cpp | 2 +- apps/openmw/mwgui/savegamedialog.cpp | 2 +- apps/openmw/mwgui/scrollwindow.cpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 2 +- apps/openmw/mwgui/spellbuyingwindow.cpp | 2 +- apps/openmw/mwgui/spellcreationdialog.cpp | 6 +++--- apps/openmw/mwgui/textinput.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 2 +- apps/openmw/mwgui/trainingwindow.cpp | 2 +- apps/openmw/mwgui/travelwindow.cpp | 2 +- apps/openmw/mwgui/waitdialog.cpp | 2 +- 28 files changed, 40 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 35ff668df0..782994b858 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -95,7 +95,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.x = "#{sCreate}"; mControllerButtons.y = "#{sMagicEffects}"; mControllerButtons.r3 = "#{sInfo}"; diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index beba6e5968..0ad31680b7 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -68,7 +68,7 @@ namespace MWGui mControllerButtons.l1 = "#{sPrev}"; mControllerButtons.r1 = "#{sNext}"; - mControllerButtons.b = "#{sClose}"; + mControllerButtons.b = "#{Interface:Close}"; center(); } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 121793c3af..b0dbc6c779 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -885,7 +885,7 @@ namespace MWGui cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } SelectSpecializationDialog::~SelectSpecializationDialog() {} @@ -967,7 +967,7 @@ namespace MWGui mAttributeButtons[0]->setStateSelected(true); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } } @@ -1067,7 +1067,7 @@ namespace MWGui mSkillButtons[0]->setStateSelected(true); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } } @@ -1156,7 +1156,7 @@ namespace MWGui // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); - mControllerButtons.a = "#{sOk}"; + mControllerButtons.a = "#{Interface:OK}"; } DescriptionDialog::~DescriptionDialog() {} diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 5053ebbde4..fcbaf146ec 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -65,7 +65,7 @@ namespace MWGui setCoord(200, 0, 600, 300); mControllerButtons.a = "#{sTake}"; - mControllerButtons.b = "#{sClose}"; + mControllerButtons.b = "#{Interface:Close}"; mControllerButtons.r3 = "#{sInfo}"; mControllerButtons.l2 = "#{sInventory}"; } diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index f858b9628a..b0a0f62580 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -23,8 +23,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - mControllerButtons.a = "#{sOk}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.a = "#{Interface:OK}"; + mControllerButtons.b = "#{Interface:Cancel}"; } } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 6efff1517d..84fd945e79 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -60,7 +60,7 @@ namespace MWGui setCoord(200, 0, 600, 300); mControllerButtons.a = "#{sTake}"; - mControllerButtons.b = "#{sClose}"; + mControllerButtons.b = "#{Interface:Close}"; mControllerButtons.x = "#{sTakeAll}"; mControllerButtons.r3 = "#{sInfo}"; mControllerButtons.l2 = "#{sInventory}"; diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 1f31871e33..063bf061c9 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -28,8 +28,8 @@ namespace MWGui // make sure we read the enter key being pressed to accept multiple items mItemEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &CountDialog::onEnterKeyPressed); - mControllerButtons.a = "#{sOk}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.a = "#{Interface:OK}"; + mControllerButtons.b = "#{Interface:Cancel}"; } void CountDialog::openCountDialog(const std::string& item, const std::string& message, const int maxCount) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 49ceafbfd8..587c4d03c5 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -91,7 +91,7 @@ namespace MWGui mDisableGamepadCursor = Settings::gui().mControllerMenus; mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } void PersuasionDialog::adjustAction(MyGUI::Widget* action, int& totalHeight) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 7a1c1532f6..056862fdce 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -61,7 +61,7 @@ namespace MWGui mName->eventEditSelectAccept += MyGUI::newDelegate(this, &EnchantingDialog::onAccept); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.y = "#{OMWEngine:EnchantType}"; mControllerButtons.l1 = "#{sItem}"; mControllerButtons.r1 = "#{sSoulGem}"; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0a5715334a..37e3f0a65d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -983,21 +983,21 @@ namespace MWGui { case MWGui::GM_Companion: mControllerButtons.a = "#{OMWEngine:InventorySelect}"; - mControllerButtons.b = "#{sClose}"; + mControllerButtons.b = "#{Interface:Close}"; mControllerButtons.x.clear(); mControllerButtons.y.clear(); mControllerButtons.r2 = "#{sCompanionShare}"; break; case MWGui::GM_Container: mControllerButtons.a = "#{OMWEngine:InventorySelect}"; - mControllerButtons.b = "#{sClose}"; + mControllerButtons.b = "#{Interface:Close}"; mControllerButtons.x = "#{sTakeAll}"; mControllerButtons.y.clear(); mControllerButtons.r2 = "#{sContainer}"; break; case MWGui::GM_Barter: mControllerButtons.a = "#{sSell}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.x = "#{sOffer}"; mControllerButtons.y.clear(); mControllerButtons.r2 = "#{sBarter}"; diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index ad2b141d8d..8dc806fdd2 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -30,7 +30,7 @@ namespace MWGui center(); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.r3 = "#{sInfo}"; } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 9b7b8096e5..90db26a519 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -698,7 +698,7 @@ namespace MWGui::ControllerButtonStr* getControllerButtons() override { - mControllerButtons.b = mOptionsMode || mStates.size() > 1 ? "#{sBack}" : "#{sClose}"; + mControllerButtons.b = mOptionsMode || mStates.size() > 1 ? "#{sBack}" : "#{Interface:Close}"; mControllerButtons.l1 = mOptionsMode ? "" : "#{sPrev}"; mControllerButtons.r1 = mOptionsMode ? "" : "#{sNext}"; mControllerButtons.r3 = mOptionsMode && mQuestMode ? "#{OMWEngine:JournalShowAll}" : ""; diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 38cfbc1e58..6d9f5fd34b 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1471,8 +1471,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.a = "#{sOk}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.a = "#{Interface:OK}"; + mControllerButtons.b = "#{Interface:Cancel}"; } } diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index db0f3bf1a2..58b05cf612 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -33,7 +33,7 @@ namespace MWGui { mDisableGamepadCursor = true; mControllerButtons.a = "#{sRepair}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } } diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 8020a4c3d5..d1f0727971 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -285,7 +285,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - mControllerButtons.a = "#{sOk}"; + mControllerButtons.a = "#{Interface:OK}"; // If we have more than one button, we need to set the focus to the first one. if (mButtons.size() > 1) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 419cb26420..786ab197c8 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -62,7 +62,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sOK}"; + mControllerButtons.b = "#{Interface:OK}"; } } @@ -537,7 +537,7 @@ namespace MWGui mDisableGamepadCursor = true; mItemButton->setStateSelected(true); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } center(); @@ -683,7 +683,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } center(); diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 2dfb47fa23..9c1e71c3fd 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -41,7 +41,7 @@ namespace MWGui mGemIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onSelectItem); mControllerButtons.a = "#{OMWEngine:RechargeSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.y = "#{sSoulGem}"; } diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 8962ae2abd..13a3923d07 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -41,7 +41,7 @@ namespace MWGui mToolIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onSelectItem); mControllerButtons.a = "#{sRepair}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.y = "#{OMWEngine:RepairTool}"; } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index b096c5c9c9..35a6de2950 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -68,7 +68,7 @@ namespace MWGui mDeleteButton->setNeedKeyFocus(false); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } void SaveGameDialog::onSlotActivated(MyGUI::ListBox* sender, size_t pos) diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 59fb1f2d20..3986aa44bd 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -40,7 +40,7 @@ namespace MWGui mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); mControllerScrollWidget = mTextView; - mControllerButtons.b = "#{sClose}"; + mControllerButtons.b = "#{Interface:Close}"; mControllerButtons.dpad = "#{sScrolldown}"; center(); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 406beb506f..11a06c7b44 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -461,7 +461,7 @@ namespace MWGui } mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sOk}"; + mControllerButtons.b = "#{Interface:OK}"; mControllerButtons.lStick = "#{sMouse}"; } diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 4b6811e605..1f6270baf0 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -39,7 +39,7 @@ namespace MWGui { mDisableGamepadCursor = true; mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.r3 = "#{sInfo}"; } } diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 976c2eebd8..25d8499193 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -101,8 +101,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; - mControllerButtons.x = "#{sOk}"; + mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.x = "#{Interface:OK}"; } } @@ -599,7 +599,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.x = "#{sBuy}"; mControllerButtons.r3 = "#{sInfo}"; } diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 7612cf4c67..9e75d0ba01 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -26,7 +26,7 @@ namespace MWGui // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); - mControllerButtons.a = "#{sOk}"; + mControllerButtons.a = "#{Interface:OK}"; } void TextInputDialog::setNextButtonShow(bool shown) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index b15311e167..ab0b58933a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -189,7 +189,7 @@ namespace MWGui SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.x = "#{sOffer}"; mControllerButtons.r3 = "#{sInfo}"; mControllerButtons.l2 = "#{sInventory}"; diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index be8359cde5..ef11d8f16c 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -41,7 +41,7 @@ namespace MWGui { mDisableGamepadCursor = true; mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } } diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 38eee1b85c..185a7be3cf 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -42,7 +42,7 @@ namespace MWGui { mDisableGamepadCursor = true; mControllerButtons.a = "#{sTravel}"; - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; } } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index fcd7b21181..0943863a69 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -80,7 +80,7 @@ namespace MWGui mTimeAdvancer.eventInterrupted += MyGUI::newDelegate(this, &WaitDialog::onWaitingInterrupted); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &WaitDialog::onWaitingFinished); - mControllerButtons.b = "#{sCancel}"; + mControllerButtons.b = "#{Interface:Cancel}"; mDisableGamepadCursor = Settings::gui().mControllerMenus; } From cf240632261d5ead70df89626ea380fb3c29047e Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 15 Jul 2025 01:33:52 -0700 Subject: [PATCH 030/102] Replace more C-style casts with C++ style --- apps/openmw/mwgui/countdialog.cpp | 4 ++-- apps/openmw/mwgui/itemview.cpp | 6 +++--- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 063bf061c9..db34b082e0 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -59,7 +59,7 @@ namespace MWGui void CountDialog::setCount(int count) { - count = std::clamp(count, 1, (int)mSlider->getScrollRange()); + count = std::clamp(count, 1, static_cast(mSlider->getScrollRange())); mSlider->setScrollPosition(count - 1); mItemEdit->setValue(count); } @@ -105,7 +105,7 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) setCount(1); else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) - setCount((int)mSlider->getScrollRange()); + setCount(static_cast(mSlider->getScrollRange())); else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false); else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 0afb3d3d76..a01eaad2e8 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -55,7 +55,7 @@ namespace MWGui mRows = std::max(maxHeight / 42, 1); mItemCount = dragArea->getChildCount(); - bool showScrollbar = int(std::ceil(mItemCount / float(mRows))) > mScrollView->getWidth() / 42; + bool showScrollbar = static_cast(std::ceil(mItemCount / float(mRows))) > mScrollView->getWidth() / 42; if (showScrollbar) { maxHeight -= 18; @@ -264,14 +264,14 @@ namespace MWGui if (prevFocus >= 0 && prevFocus < mItemCount) { - ItemWidget* prev = (ItemWidget*)dragArea->getChildAt(prevFocus); + ItemWidget* prev = static_cast(dragArea->getChildAt(prevFocus)); if (prev) prev->setControllerFocus(false); } if (mControllerActiveWindow && newFocus >= 0 && newFocus < mItemCount) { - ItemWidget* focused = (ItemWidget*)dragArea->getChildAt(newFocus); + ItemWidget* focused = static_cast(dragArea->getChildAt(newFocus)); if (focused) { focused->setControllerFocus(true); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ee9df120c8..78ea13af35 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -899,7 +899,8 @@ namespace MWGui if (state.mWindows.size() == 0) return nullptr; - int activeIndex = std::clamp(mActiveControllerWindows[mode], 0, (int)state.mWindows.size() - 1); + int activeIndex + = std::clamp(mActiveControllerWindows[mode], 0, static_cast(state.mWindows.size()) - 1); // If the active window is no longer visible, find the next visible window. if (!state.mWindows[activeIndex]->isVisible()) From 4adb9bd8ac825442c5d539b9ca5c64d4aed27ef4 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 15 Jul 2025 02:02:49 -0700 Subject: [PATCH 031/102] Unhide the cursor if it was hidden to show a controller tooltip --- apps/openmw/mwinput/mousemanager.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index 540c334b1e..c8b0023437 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -57,7 +57,8 @@ namespace MWInput // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor - float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor(); + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + float uiScale = winMgr->getScalingFactor(); mGuiCursorX = static_cast(arg.x) / uiScale; mGuiCursorY = static_cast(arg.y) / uiScale; @@ -70,7 +71,13 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseMove( static_cast(mGuiCursorX), static_cast(mGuiCursorY), mMouseWheel); - MWBase::Environment::get().getWindowManager()->setCursorActive(true); + winMgr->setCursorActive(true); + if (Settings::gui().mControllerMenus && !winMgr->getCursorVisible()) + { + // Unhide the cursor if it was hidden to show a controller tooltip. + winMgr->setControllerTooltip(false); + winMgr->setCursorVisible(true); + } } if (mMouseLookEnabled && !input->controlsDisabled()) From f50c2751338e8c2a2fe6afa6af0ca3a620ebee6a Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Wed, 16 Jul 2025 00:25:45 -0700 Subject: [PATCH 032/102] Ensure mouse really moved before unhiding cursor in controller menu --- apps/openmw/mwinput/mousemanager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index c8b0023437..b17b92e118 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -72,7 +72,8 @@ namespace MWInput static_cast(mGuiCursorX), static_cast(mGuiCursorY), mMouseWheel); winMgr->setCursorActive(true); - if (Settings::gui().mControllerMenus && !winMgr->getCursorVisible()) + if (Settings::gui().mControllerMenus && !winMgr->getCursorVisible() + && (std::abs(arg.xrel) > 1 || std::abs(arg.yrel) > 1)) { // Unhide the cursor if it was hidden to show a controller tooltip. winMgr->setControllerTooltip(false); From 7d5a9a2e2f3a49a2cbb6f33eef99a5c2f66d53fb Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Thu, 17 Jul 2025 11:26:36 -0700 Subject: [PATCH 033/102] CLEANUP: Only increment highest refNum for ones which are actually higher --- apps/opencs/model/world/refcollection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 5d2ed2cfc5..b2b6cec245 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -175,7 +175,7 @@ void CSMWorld::RefCollection::load(ESM::ESMReader& reader, int cellIndex, bool b ref.mIdNum = mNextId; // FIXME: fragile ref.mId = ESM::RefId::stringRefId(getNewId()); - if (!base && ref.mRefNum.mIndex >= mHighestUsedRefNum) + if (!base && ref.mRefNum.mIndex > mHighestUsedRefNum) mHighestUsedRefNum = ref.mRefNum.mIndex; cache.emplace(ref.mRefNum, ref.mIdNum); From 0abbf9147702091df7ab11cd1a3c7a002bc9ee7f Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 17 Jul 2025 23:07:54 -0700 Subject: [PATCH 034/102] Disable double-clicking on window titles in the inventory menu --- apps/openmw/mwgui/inventorywindow.cpp | 4 +++- apps/openmw/mwgui/mapwindow.cpp | 4 +++- apps/openmw/mwgui/spellwindow.cpp | 4 +++- apps/openmw/mwgui/statswindow.cpp | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 37e3f0a65d..c15494d2c3 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -594,7 +594,9 @@ namespace MWGui void InventoryWindow::onTitleDoubleClicked() { - if (MyGUI::InputManager::getInstance().isShiftPressed()) + if (Settings::gui().mControllerMenus && mGuiMode == GM_Inventory) + return; + else if (MyGUI::InputManager::getInstance().isShiftPressed()) toggleMaximized(); else if (!mPinned) MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 6d9f5fd34b..0774e6ccdf 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1243,7 +1243,9 @@ namespace MWGui void MapWindow::onTitleDoubleClicked() { - if (MyGUI::InputManager::getInstance().isShiftPressed()) + if (Settings::gui().mControllerMenus) + return; + else if (MyGUI::InputManager::getInstance().isShiftPressed()) MWBase::Environment::get().getWindowManager()->toggleMaximized(this); else if (!mPinned) MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index c8d9b2acd9..dee81a5ded 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -76,7 +76,9 @@ namespace MWGui void SpellWindow::onTitleDoubleClicked() { - if (MyGUI::InputManager::getInstance().isShiftPressed()) + if (Settings::gui().mControllerMenus) + return; + else if (MyGUI::InputManager::getInstance().isShiftPressed()) MWBase::Environment::get().getWindowManager()->toggleMaximized(this); else if (!mPinned) MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 430101f069..3f60e0e49f 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -723,7 +723,9 @@ namespace MWGui void StatsWindow::onTitleDoubleClicked() { - if (MyGUI::InputManager::getInstance().isShiftPressed()) + if (Settings::gui().mControllerMenus) + return; + else if (MyGUI::InputManager::getInstance().isShiftPressed()) { MWBase::Environment::get().getWindowManager()->toggleMaximized(this); MyGUI::Window* t = mMainWidget->castType(); From a73b803592881c785bb9db724668c917cc72b7d1 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 18 Jul 2025 01:05:48 -0700 Subject: [PATCH 035/102] Fix crash when spell buying window is empty --- apps/openmw/mwgui/spellbuyingwindow.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 1f6270baf0..5f9914c4d9 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -147,12 +147,14 @@ namespace MWGui { mControllerFocus = 0; if (mSpellButtons.size() > 0) + { mSpellButtons[0].first->setStateSelected(true); - MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); - winMgr->setControllerTooltip(Settings::gui().mControllerTooltips); - if (winMgr->getControllerTooltip()) - MWBase::Environment::get().getInputManager()->warpMouseToWidget(mSpellButtons[0].first); + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + winMgr->setControllerTooltip(Settings::gui().mControllerTooltips); + if (winMgr->getControllerTooltip()) + MWBase::Environment::get().getInputManager()->warpMouseToWidget(mSpellButtons[0].first); + } } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the From c53b1bf2c0ead839b08a17da3f0f629207b8739e Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 18 Jul 2025 01:06:25 -0700 Subject: [PATCH 036/102] Replace more C-style casts with C++ variants --- apps/openmw/mwgui/bookpage.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 12 ++++-------- apps/openmw/mwgui/spellcreationdialog.cpp | 4 ++-- apps/openmw/mwgui/spellview.cpp | 6 +++--- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 2c58ca3b72..ef296534c4 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -1302,7 +1302,7 @@ namespace MWGui void setFocusItem(BookTypesetter::Style* itemStyle) override { - mPageDisplay->mFocusItem = (TypesetBookImpl::StyleImpl*)itemStyle; + mPageDisplay->mFocusItem = static_cast(itemStyle); mPageDisplay->dirtyFocusItem(); } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index c15494d2c3..6b1ea84c68 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -1046,20 +1046,16 @@ namespace MWGui { // Take all. Pass the button press to the container window and let it do the // logic of taking all. - MWGui::ContainerWindow* containerWindow = (MWGui::ContainerWindow*)MWBase::Environment::get() - .getWindowManager() - ->getGuiModeWindows(mGuiMode) - .at(0); + MWGui::ContainerWindow* containerWindow = static_cast( + MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0)); containerWindow->onControllerButtonEvent(arg); } else if (mGuiMode == MWGui::GM_Barter) { // Offer. Pass the button press to the barter window and let it do the logic // of making an offer. - MWGui::TradeWindow* tradeWindow = (MWGui::TradeWindow*)MWBase::Environment::get() - .getWindowManager() - ->getGuiModeWindows(mGuiMode) - .at(1); + MWGui::TradeWindow* tradeWindow = static_cast( + MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(1)); tradeWindow->onControllerButtonEvent(arg); } } diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 25d8499193..190793b77a 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -556,7 +556,7 @@ namespace MWGui } else { - ((MyGUI::Button*)button)->setStateSelected(false); + static_cast(button)->setStateSelected(false); } } @@ -570,7 +570,7 @@ namespace MWGui } else { - ((MyGUI::Button*)button)->setStateSelected(true); + static_cast(button)->setStateSelected(true); } } } diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 44bdcffe07..6ae757b7be 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -143,7 +143,7 @@ namespace MWGui mLines.emplace_back(t, costChance, i); } else - mLines.emplace_back(t, (MyGUI::Widget*)nullptr, i); + mLines.emplace_back(t, static_cast(nullptr), i); t->setStateSelected(spell.mSelected); } @@ -252,7 +252,7 @@ namespace MWGui MyGUI::ImageBox* separator = mScrollView->createWidget( "MW_HLine", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18), MyGUI::Align::Left | MyGUI::Align::Top); separator->setNeedMouseFocus(false); - mLines.emplace_back(separator, (MyGUI::Widget*)nullptr, NoSpellIndex); + mLines.emplace_back(separator, static_cast(nullptr), NoSpellIndex); } MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", @@ -272,7 +272,7 @@ namespace MWGui mLines.emplace_back(groupWidget, groupWidget2, NoSpellIndex); } else - mLines.emplace_back(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex); + mLines.emplace_back(groupWidget, static_cast(nullptr), NoSpellIndex); mGroupIndices.push_back(mButtons.size()); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 78ea13af35..0138811691 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1922,7 +1922,8 @@ namespace MWGui // If using controller menus, don't persist changes to size of the stats or magic // windows. if (Settings::gui().mControllerMenus - && (window == (MyGUI::Window*)mStatsWindow || window == (MyGUI::Window*)mSpellWindow)) + && (window == mStatsWindow->mMainWidget->castType() + || window == mSpellWindow->mMainWidget->castType())) return; const auto it = mTrackedWindows.find(window); From bf327adc9394a99ed8d3b8dc31e3fc26cdd5a02d Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 18 Jul 2025 17:53:29 -0700 Subject: [PATCH 037/102] Bump OPENMW_LUA_API_REVISION and gamepad controls versions --- CMakeLists.txt | 2 +- files/data/scripts/omw/input/gamepadcontrols.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6103ddcb33..78d9aae646 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 82) +set(OPENMW_LUA_API_REVISION 83) set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "") diff --git a/files/data/scripts/omw/input/gamepadcontrols.lua b/files/data/scripts/omw/input/gamepadcontrols.lua index 75ca494a27..594e89e6ad 100644 --- a/files/data/scripts/omw/input/gamepadcontrols.lua +++ b/files/data/scripts/omw/input/gamepadcontrols.lua @@ -11,7 +11,7 @@ return { interface = { --- Interface version -- @field [parent=#GamepadControls] #number version - version = 0, + version = 1, --- Checks if the gamepad cursor is active. If it is active, the left stick can move the cursor, and A will be interpreted as a mouse click. -- @function [parent=#GamepadControls] isGamepadCursorActive From dcac39aefd7849df08620ab10269472a711b15bf Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sat, 19 Jul 2025 09:44:01 -0700 Subject: [PATCH 038/102] FIX: never serialize MVRF for refferences from the local plugin --- apps/opencs/model/doc/savingstages.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index edd4329f7f..074163c31e 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -304,6 +304,8 @@ void CSMDoc::WriteCellCollectionStage::writeReferences( { CSMWorld::CellRef refRecord = ref.get(); + const bool isLocal = refRecord.mRefNum.mContentFile == 0; + // -1 is the current file, saved indices are 1-based refRecord.mRefNum.mContentFile++; @@ -316,7 +318,8 @@ void CSMDoc::WriteCellCollectionStage::writeReferences( } ESM::RefId streamId = ESM::RefId::stringRefId(stream.str()); - if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) != streamId && !interior) + if (!isLocal && (refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) != streamId + && !interior) { // An empty mOriginalCell is meant to indicate that it is the same as // the current cell. It is possible that a moved ref is moved again. From c2ad1df0301e1bc3d5fd346ccbbd9e82f66c8fb3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 19 Jul 2025 14:03:26 -0700 Subject: [PATCH 039/102] Update Swedish translation for controller tooltips option --- files/data/l10n/OMWEngine/sv.yaml | 2 +- files/lang/launcher_sv.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/files/data/l10n/OMWEngine/sv.yaml b/files/data/l10n/OMWEngine/sv.yaml index 1617c65956..c5ad166849 100644 --- a/files/data/l10n/OMWEngine/sv.yaml +++ b/files/data/l10n/OMWEngine/sv.yaml @@ -217,7 +217,7 @@ WobblyShores: "Vaggande stränder" # Controller button names -EnchantType: "Typ" +EnchantType: "Typ av förtrollning" InventorySelect: "Placera" JournalQuests: "Uppdrag" JournalShowAll: "Visa Alla" diff --git a/files/lang/launcher_sv.ts b/files/lang/launcher_sv.ts index c540f28eac..268847dfeb 100644 --- a/files/lang/launcher_sv.ts +++ b/files/lang/launcher_sv.ts @@ -1488,11 +1488,11 @@ de ordinarie fonterna i Morrowind. Bocka denna ruta om du ändå föredrar ordin <html><head/><body><p>When using controller menus, make tooltips visible by default.</p></body></html> - <html><head/><body><p>När du använder kontrollmenyer, synliggör verktygstips som standard.</p></body></html> + <html><head/><body><p>Visar inforutor som standard när handkontrollmenyer används.</p></body></html> Show Controller Tooltips By Default - Visa verktygstips för kontrollenheter som standard + Visa handkontrollinforutor som standard From f8d9149e4faf58c1d08bdd3447b1eb606146e0ac Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 19 Jul 2025 16:31:25 -0700 Subject: [PATCH 040/102] Use enum and array for cleaner management of controller button overlay --- .../openmw/mwgui/controllerbuttonsoverlay.cpp | 116 +++++++----------- .../openmw/mwgui/controllerbuttonsoverlay.hpp | 71 +++++------ 2 files changed, 69 insertions(+), 118 deletions(-) diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index aee2ae2631..197b11f698 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -11,65 +11,28 @@ namespace MWGui { MWBase::InputManager* inputMgr = MWBase::Environment::get().getInputManager(); - getWidget(mImageA, "BtnAImage"); - getWidget(mTextA, "BtnAText"); - setIcon(mImageA, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_A)); + mButtons[Button::Button_A] = { "A", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_A) }; + mButtons[Button::Button_B] = { "B", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_B) }; + mButtons[Button::Button_Dpad] = { "Dpad", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_DPAD_UP) }; + mButtons[Button::Button_L1] = { "L1", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_LEFTSHOULDER) }; + mButtons[Button::Button_L2] = { "L2", inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERLEFT) }; + mButtons[Button::Button_L3] = { "L3", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_LEFTSTICK) }; + mButtons[Button::Button_LStick] = { "LStick", inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_LEFTY) }; + mButtons[Button::Button_Menu] = { "Menu", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_BACK) }; + mButtons[Button::Button_R1] = { "R1", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) }; + mButtons[Button::Button_R2] = { "R2", inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERRIGHT) }; + mButtons[Button::Button_R3] = { "R3", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_RIGHTSTICK) }; + mButtons[Button::Button_RStick] = { "RStick", inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_RIGHTY) }; + mButtons[Button::Button_View] = { "View", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_START) }; + mButtons[Button::Button_X] = { "X", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_X) }; + mButtons[Button::Button_Y] = { "Y", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_Y) }; - getWidget(mImageB, "BtnBImage"); - getWidget(mTextB, "BtnBText"); - setIcon(mImageB, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_B)); - - getWidget(mImageDpad, "BtnDpadImage"); - getWidget(mTextDpad, "BtnDpadText"); - setIcon(mImageDpad, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_DPAD_UP)); - - getWidget(mImageL1, "BtnL1Image"); - getWidget(mTextL1, "BtnL1Text"); - setIcon(mImageL1, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); - - getWidget(mImageL2, "BtnL2Image"); - getWidget(mTextL2, "BtnL2Text"); - setIcon(mImageL2, inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERLEFT)); - - getWidget(mImageL3, "BtnL3Image"); - getWidget(mTextL3, "BtnL3Text"); - setIcon(mImageL3, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_LEFTSTICK)); - - getWidget(mImageLStick, "BtnLStickImage"); - getWidget(mTextLStick, "BtnLStickText"); - setIcon(mImageLStick, inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_LEFTY)); - - getWidget(mImageMenu, "BtnMenuImage"); - getWidget(mTextMenu, "BtnMenuText"); - setIcon(mImageMenu, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_BACK)); - - getWidget(mImageR1, "BtnR1Image"); - getWidget(mTextR1, "BtnR1Text"); - setIcon(mImageR1, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); - - getWidget(mImageR2, "BtnR2Image"); - getWidget(mTextR2, "BtnR2Text"); - setIcon(mImageR2, inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERRIGHT)); - - getWidget(mImageR3, "BtnR3Image"); - getWidget(mTextR3, "BtnR3Text"); - setIcon(mImageR3, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_RIGHTSTICK)); - - getWidget(mImageRStick, "BtnRStickImage"); - getWidget(mTextRStick, "BtnRStickText"); - setIcon(mImageRStick, inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_RIGHTY)); - - getWidget(mImageView, "BtnViewImage"); - getWidget(mTextView, "BtnViewText"); - setIcon(mImageView, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_START)); - - getWidget(mImageX, "BtnXImage"); - getWidget(mTextX, "BtnXText"); - setIcon(mImageX, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_X)); - - getWidget(mImageY, "BtnYImage"); - getWidget(mTextY, "BtnYText"); - setIcon(mImageY, inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_Y)); + for (size_t i = 0; i < mButtons.size(); i++) + { + getWidget(mButtons[i].mImage, "Btn" + mButtons[i].mName + "Image"); + getWidget(mButtons[i].mText, "Btn" + mButtons[i].mName + "Text"); + setIcon(mButtons[i].mImage, mButtons[i].mImagePath); + } getWidget(mHBox, "ButtonBox"); } @@ -77,21 +40,22 @@ namespace MWGui void ControllerButtonsOverlay::setButtons(ControllerButtonStr* buttons) { int buttonCount = 0; - buttonCount += updateButton(mTextA, mImageA, buttons->a); - buttonCount += updateButton(mTextB, mImageB, buttons->b); - buttonCount += updateButton(mTextDpad, mImageDpad, buttons->dpad); - buttonCount += updateButton(mTextL1, mImageL1, buttons->l1); - buttonCount += updateButton(mTextL2, mImageL2, buttons->l2); - buttonCount += updateButton(mTextL3, mImageL3, buttons->l3); - buttonCount += updateButton(mTextLStick, mImageLStick, buttons->lStick); - buttonCount += updateButton(mTextMenu, mImageMenu, buttons->menu); - buttonCount += updateButton(mTextR1, mImageR1, buttons->r1); - buttonCount += updateButton(mTextR2, mImageR2, buttons->r2); - buttonCount += updateButton(mTextR3, mImageR3, buttons->r3); - buttonCount += updateButton(mTextRStick, mImageRStick, buttons->rStick); - buttonCount += updateButton(mTextView, mImageView, buttons->view); - buttonCount += updateButton(mTextX, mImageX, buttons->x); - buttonCount += updateButton(mTextY, mImageY, buttons->y); + buttonCount += updateButton(Button::Button_A, buttons->a); + buttonCount += updateButton(Button::Button_B, buttons->b); + buttonCount += updateButton(Button::Button_Dpad, buttons->dpad); + buttonCount += updateButton(Button::Button_L1, buttons->l1); + buttonCount += updateButton(Button::Button_L2, buttons->l2); + buttonCount += updateButton(Button::Button_L3, buttons->l3); + buttonCount += updateButton(Button::Button_LStick, buttons->lStick); + buttonCount += updateButton(Button::Button_Menu, buttons->menu); + buttonCount += updateButton(Button::Button_R1, buttons->r1); + buttonCount += updateButton(Button::Button_R2, buttons->r2); + buttonCount += updateButton(Button::Button_R3, buttons->r3); + buttonCount += updateButton(Button::Button_RStick, buttons->rStick); + buttonCount += updateButton(Button::Button_View, buttons->view); + buttonCount += updateButton(Button::Button_X, buttons->x); + buttonCount += updateButton(Button::Button_Y, buttons->y); + mHBox->notifyChildrenSizeChanged(); setVisible(buttonCount > 0); @@ -103,9 +67,11 @@ namespace MWGui image->setImageTexture(imagePath); } - int ControllerButtonsOverlay::updateButton( - MyGUI::TextBox* text, MyGUI::ImageBox* image, const std::string& buttonStr) + int ControllerButtonsOverlay::updateButton(ControllerButtonsOverlay::Button button, const std::string& buttonStr) { + MyGUI::TextBox* text = mButtons[button].mText; + MyGUI::ImageBox* image = mButtons[button].mImage; + if (buttonStr.empty()) { image->setVisible(false); diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index 700cf0c147..1e64da6565 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -18,55 +18,40 @@ namespace MWGui void setButtons(ControllerButtonStr* buttons); private: - MyGUI::ImageBox* mImageA; - MyGUI::TextBox* mTextA; + enum Button + { + Button_A = 0, + Button_B, + Button_Dpad, + Button_L1, + Button_L2, + Button_L3, + Button_LStick, + Button_Menu, + Button_R1, + Button_R2, + Button_R3, + Button_RStick, + Button_View, + Button_X, + Button_Y, + Button_Max, + }; - MyGUI::ImageBox* mImageB; - MyGUI::TextBox* mTextB; + struct ButtonDetails + { + std::string mName; + std::string mImagePath; + MyGUI::ImageBox* mImage = nullptr; + MyGUI::TextBox* mText = nullptr; + }; - MyGUI::ImageBox* mImageDpad; - MyGUI::TextBox* mTextDpad; - - MyGUI::ImageBox* mImageL1; - MyGUI::TextBox* mTextL1; - - MyGUI::ImageBox* mImageL2; - MyGUI::TextBox* mTextL2; - - MyGUI::ImageBox* mImageL3; - MyGUI::TextBox* mTextL3; - - MyGUI::ImageBox* mImageLStick; - MyGUI::TextBox* mTextLStick; - - MyGUI::ImageBox* mImageMenu; - MyGUI::TextBox* mTextMenu; - - MyGUI::ImageBox* mImageR1; - MyGUI::TextBox* mTextR1; - - MyGUI::ImageBox* mImageR2; - MyGUI::TextBox* mTextR2; - - MyGUI::ImageBox* mImageR3; - MyGUI::TextBox* mTextR3; - - MyGUI::ImageBox* mImageRStick; - MyGUI::TextBox* mTextRStick; - - MyGUI::ImageBox* mImageView; - MyGUI::TextBox* mTextView; - - MyGUI::ImageBox* mImageX; - MyGUI::TextBox* mTextX; - - MyGUI::ImageBox* mImageY; - MyGUI::TextBox* mTextY; + std::array mButtons; Gui::HBox* mHBox; void setIcon(MyGUI::ImageBox* image, const std::string& imagePath); - int updateButton(MyGUI::TextBox* text, MyGUI::ImageBox* image, const std::string& buttonStr); + int updateButton(Button button, const std::string& buttonStr); }; } From 29f1c7c68f2cfc60df1477ec1f3ade01fdc2426b Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 19 Jul 2025 17:12:09 -0700 Subject: [PATCH 041/102] Remove some hardcoded menu sizes for inventory mode --- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/controllerbuttonsoverlay.cpp | 8 ++++++++ apps/openmw/mwgui/controllerbuttonsoverlay.hpp | 1 + apps/openmw/mwgui/inventorytabsoverlay.cpp | 7 +++++++ apps/openmw/mwgui/inventorytabsoverlay.hpp | 1 + apps/openmw/mwgui/inventorywindow.cpp | 6 ++++-- apps/openmw/mwgui/mapwindow.cpp | 5 +++-- apps/openmw/mwgui/spellwindow.cpp | 8 +++++--- apps/openmw/mwgui/statswindow.cpp | 7 ++++--- apps/openmw/mwgui/statswindow.hpp | 4 ++++ apps/openmw/mwgui/windowmanagerimp.cpp | 10 ++++++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 1 + 12 files changed, 50 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 8a9bd3dab9..b7380e6442 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -387,6 +387,8 @@ namespace MWBase /// Return the window that should receive controller events virtual MWGui::WindowBase* getActiveControllerWindow() = 0; + /// Return the available height for menus accounting for visible controller overlays + virtual int getControllerMenuHeight() = 0; /// Cycle to the next window to receive controller events virtual void cycleActiveControllerWindow(bool next) = 0; virtual void setActiveControllerWindow(MWGui::GuiMode mode, int activeIndex) = 0; diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index 197b11f698..dc3534e1b9 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -1,5 +1,7 @@ #include "controllerbuttonsoverlay.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" @@ -37,6 +39,12 @@ namespace MWGui getWidget(mHBox, "ButtonBox"); } + int ControllerButtonsOverlay::getHeight() + { + MyGUI::Window* window = mMainWidget->castType(); + return window->getHeight(); + } + void ControllerButtonsOverlay::setButtons(ControllerButtonStr* buttons) { int buttonCount = 0; diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index 1e64da6565..44f93580a3 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -15,6 +15,7 @@ namespace MWGui public: ControllerButtonsOverlay(); + int getHeight(); void setButtons(ControllerButtonStr* buttons); private: diff --git a/apps/openmw/mwgui/inventorytabsoverlay.cpp b/apps/openmw/mwgui/inventorytabsoverlay.cpp index 062e7dbcb4..fb9c7dec3d 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.cpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.cpp @@ -1,6 +1,7 @@ #include "inventorytabsoverlay.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" @@ -39,6 +40,12 @@ namespace MWGui MWBase::Environment::get().getInputManager()->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERRIGHT)); } + int InventoryTabsOverlay::getHeight() + { + MyGUI::Window* window = mMainWidget->castType(); + return window->getHeight(); + } + void InventoryTabsOverlay::onTabClicked(MyGUI::Widget* sender) { if (!MWBase::Environment::get().getWindowManager()->getJournalAllowed()) diff --git a/apps/openmw/mwgui/inventorytabsoverlay.hpp b/apps/openmw/mwgui/inventorytabsoverlay.hpp index 5368d9710f..1f5cf39607 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.hpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.hpp @@ -12,6 +12,7 @@ namespace MWGui public: InventoryTabsOverlay(); + int getHeight(); void setTab(int index); private: diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 6b1ea84c68..dcd9d1dc74 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -42,6 +42,7 @@ #include "itemview.hpp" #include "settings.hpp" #include "sortfilteritemmodel.hpp" +#include "statswindow.hpp" #include "tooltips.hpp" #include "tradeitemmodel.hpp" #include "tradewindow.hpp" @@ -1109,13 +1110,14 @@ namespace MWGui if (!Settings::gui().mControllerMenus) return; - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory) + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + if (winMgr->getMode() == MWGui::GM_Inventory) { // Fill the screen, or limit to a certain size on large screens. Size chosen to // match the size of the stats window. MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); int width = std::min(viewSize.width, 1600); - int height = std::min(viewSize.height - 48 - 48, 750); + int height = std::min(winMgr->getControllerMenuHeight(), StatsWindow::getIdealHeight()); int x = (viewSize.width - width) / 2; int y = (viewSize.height - height) / 2; diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 0774e6ccdf..5b0e5dc7cc 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1440,13 +1440,14 @@ namespace MWGui void MapWindow::setActiveControllerWindow(bool active) { - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory) + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + if (winMgr->getMode() == MWGui::GM_Inventory) { // Fill the screen, or limit to a certain size on large screens. Size chosen to // show the entire local map without scrolling. MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); int width = std::min(viewSize.width, 1552); - int height = std::min(viewSize.height - 48 - 48, 1572); + int height = std::min(winMgr->getControllerMenuHeight(), 1572); int x = (viewSize.width - width) / 2; int y = (viewSize.height - height) / 2; diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index dee81a5ded..b5533c2f61 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -29,6 +29,7 @@ #include "confirmationdialog.hpp" #include "spellicons.hpp" #include "spellview.hpp" +#include "statswindow.hpp" namespace MWGui { @@ -313,13 +314,14 @@ namespace MWGui void SpellWindow::setActiveControllerWindow(bool active) { - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory) + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + if (winMgr->getMode() == MWGui::GM_Inventory) { // Fill the screen, or limit to a certain size on large screens. Size chosen to // match the size of the stats window. MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - int width = std::min(viewSize.width, 600); - int height = std::min(viewSize.height - 48 - 48, 750); + int width = std::min(viewSize.width, StatsWindow::getIdealWidth()); + int height = std::min(winMgr->getControllerMenuHeight(), StatsWindow::getIdealHeight()); int x = (viewSize.width - width) / 2; int y = (viewSize.height - height) / 2; diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 3f60e0e49f..1552a90295 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -745,13 +745,14 @@ namespace MWGui void StatsWindow::setActiveControllerWindow(bool active) { - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory) + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + if (winMgr->getMode() == MWGui::GM_Inventory) { // Fill the screen, or limit to a certain size on large screens. Size chosen to // show all stats. MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - int width = std::min(viewSize.width, 600); - int height = std::min(viewSize.height - 48 - 48, 750); + int width = std::min(viewSize.width, getIdealWidth()); + int height = std::min(winMgr->getControllerMenuHeight(), getIdealHeight()); int x = (viewSize.width - width) / 2; int y = (viewSize.height - height) / 2; diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 3021873aa8..c1564498c2 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -13,6 +13,10 @@ namespace MWGui public: typedef std::map FactionList; + /// It would be nice to measure these, but for now they're hardcoded. + static int getIdealHeight() { return 750; } + static int getIdealWidth() { return 600; } + StatsWindow(DragAndDrop* drag); /// automatically updates all the data in the stats window, but only if it has changed. diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 0138811691..f7093e5d44 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2621,6 +2621,16 @@ namespace MWGui return res; } + int WindowManager::getControllerMenuHeight() + { + int height = MyGUI::RenderManager::getInstance().getViewSize().height; + if (mControllerButtonsOverlay != nullptr && mControllerButtonsOverlay->isVisible()) + height -= mControllerButtonsOverlay->getHeight(); + if (mInventoryTabsOverlay != nullptr && mInventoryTabsOverlay->isVisible()) + height -= mInventoryTabsOverlay->getHeight(); + return height; + } + void WindowManager::setControllerTooltip(bool enabled) { if (!Settings::gui().mControllerMenus) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 8593a8119e..c45966ceff 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -394,6 +394,7 @@ namespace MWGui void asyncPrepareSaveMap() override; WindowBase* getActiveControllerWindow() override; + int getControllerMenuHeight() override; void cycleActiveControllerWindow(bool next) override; void setActiveControllerWindow(GuiMode mode, int activeIndex) override; bool getControllerTooltip() const override { return mControllerTooltip; } From b0bdd68e7c4899cae0155af74848c3d02ff696c2 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 19 Jul 2025 17:12:33 -0700 Subject: [PATCH 042/102] Update a comment --- apps/openmw/mwgui/alchemywindow.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 782994b858..9f493850bf 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -558,19 +558,21 @@ namespace MWGui // When the filter list combo box is open, send all inputs to it. if (arg.button == SDL_CONTROLLER_BUTTON_A || arg.button == SDL_CONTROLLER_BUTTON_Y) { - // Select the highlighted entry in the combo box and close it. + // Select the highlighted entry in the combo box and close it. List is closed by focusing on another + // widget. size_t index = mFilterValue->getIndexSelected(); mFilterValue->setIndexSelected(index); onFilterChanged(mFilterValue, index); - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); // Close list + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { - // Close the list without selecting anything + // Close the list without selecting anything. List is closed by focusing on another widget. mFilterValue->clearIndexSelected(); onFilterEdited(mFilterValue); - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); // Close list + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false); From ef50578a625544e7f9d942cef20ef5e155b43c5f Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 19 Jul 2025 18:09:00 -0700 Subject: [PATCH 043/102] Move controller button overlay padding into layout file --- .../openmw/mwgui/controllerbuttonsoverlay.cpp | 25 +- .../openmw/mwgui/controllerbuttonsoverlay.hpp | 3 +- .../mygui/openmw_controllerbuttons.layout | 377 ++++++++++-------- 3 files changed, 221 insertions(+), 184 deletions(-) diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index dc3534e1b9..ac4f95ff7c 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -31,8 +31,9 @@ namespace MWGui for (size_t i = 0; i < mButtons.size(); i++) { - getWidget(mButtons[i].mImage, "Btn" + mButtons[i].mName + "Image"); - getWidget(mButtons[i].mText, "Btn" + mButtons[i].mName + "Text"); + getWidget(mButtons[i].mImage, "Btn" + mButtons[i].mLayoutName + "Image"); + getWidget(mButtons[i].mText, "Btn" + mButtons[i].mLayoutName + "Text"); + getWidget(mButtons[i].mHBox, "Btn" + mButtons[i].mLayoutName); setIcon(mButtons[i].mImage, mButtons[i].mImagePath); } @@ -77,27 +78,17 @@ namespace MWGui int ControllerButtonsOverlay::updateButton(ControllerButtonsOverlay::Button button, const std::string& buttonStr) { - MyGUI::TextBox* text = mButtons[button].mText; - MyGUI::ImageBox* image = mButtons[button].mImage; - if (buttonStr.empty()) { - image->setVisible(false); - image->setUserString("Hidden", "true"); - - text->setVisible(false); - text->setUserString("Hidden", "true"); + mButtons[button].mHBox->setVisible(false); + mButtons[button].mHBox->setUserString("Hidden", "true"); return 0; } else { - image->setVisible(true); - image->setUserString("Hidden", "false"); - - text->setCaptionWithReplacing(buttonStr); - text->setVisible(true); - text->setUserString("Hidden", "false"); - text->setSize(text->getTextSize().width + 16, 48); + mButtons[button].mHBox->setVisible(true); + mButtons[button].mHBox->setUserString("Hidden", "false"); + mButtons[button].mText->setCaptionWithReplacing(buttonStr); return 1; } } diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index 44f93580a3..623424caf2 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -41,10 +41,11 @@ namespace MWGui struct ButtonDetails { - std::string mName; + std::string mLayoutName; std::string mImagePath; MyGUI::ImageBox* mImage = nullptr; MyGUI::TextBox* mText = nullptr; + Gui::HBox* mHBox = nullptr; }; std::array mButtons; diff --git a/files/data/mygui/openmw_controllerbuttons.layout b/files/data/mygui/openmw_controllerbuttons.layout index e0e68bf2ed..30f92b9b4b 100644 --- a/files/data/mygui/openmw_controllerbuttons.layout +++ b/files/data/mygui/openmw_controllerbuttons.layout @@ -6,203 +6,248 @@ - + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + From 0f0a402ead9ca4c070297e123dfb23b53af67b16 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 20 Jul 2025 08:53:14 +0000 Subject: [PATCH 044/102] Local content file index is actually -1 --- apps/opencs/model/doc/savingstages.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 074163c31e..85a570d8dd 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -304,7 +304,7 @@ void CSMDoc::WriteCellCollectionStage::writeReferences( { CSMWorld::CellRef refRecord = ref.get(); - const bool isLocal = refRecord.mRefNum.mContentFile == 0; + const bool isLocal = refRecord.mRefNum.mContentFile == -1; // -1 is the current file, saved indices are 1-based refRecord.mRefNum.mContentFile++; From b9ae89e03233c5609b24380f03a653e6cf8ed7bd Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 25 Jul 2025 21:46:00 -0700 Subject: [PATCH 045/102] Split onControllerButtonEvent into two; loop through ingredient slots --- apps/openmw/mwgui/alchemywindow.cpp | 120 ++++++++++++++-------------- apps/openmw/mwgui/alchemywindow.hpp | 1 + 2 files changed, 63 insertions(+), 58 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 9f493850bf..d0931057cd 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -547,6 +547,32 @@ namespace MWGui mBrewCountEdit->setValue(currentCount - 1); } + void AlchemyWindow::filterListButtonHandler(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A || arg.button == SDL_CONTROLLER_BUTTON_Y) + { + // Select the highlighted entry in the combo box and close it. List is closed by focusing on another + // widget. + size_t index = mFilterValue->getIndexSelected(); + mFilterValue->setIndexSelected(index); + onFilterChanged(mFilterValue, index); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); + + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + // Close the list without selecting anything. List is closed by focusing on another widget. + mFilterValue->clearIndexSelected(); + onFilterEdited(mFilterValue); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false); + } + bool AlchemyWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); @@ -556,71 +582,49 @@ namespace MWGui if (isFilterListOpen) { // When the filter list combo box is open, send all inputs to it. - if (arg.button == SDL_CONTROLLER_BUTTON_A || arg.button == SDL_CONTROLLER_BUTTON_Y) - { - // Select the highlighted entry in the combo box and close it. List is closed by focusing on another - // widget. - size_t index = mFilterValue->getIndexSelected(); - mFilterValue->setIndexSelected(index); - onFilterChanged(mFilterValue, index); - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); + filterListButtonHandler(arg); + return true; + } - MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); - } - else if (arg.button == SDL_CONTROLLER_BUTTON_B) + if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + // Remove active ingredients or close the window, starting with right-most slot. + for (size_t i = mIngredients.size() - 1; i >= 0; --i) { - // Close the list without selecting anything. List is closed by focusing on another widget. + if (mIngredients[i]->isUserString("ToolTipType")) + { + onIngredientSelected(mIngredients[i]); + return true; + } + } + // If the ingredients list is empty, B closes the menu. + onCancelButtonClicked(mCancelButton); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + onCreateButtonClicked(mCreateButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_Y && mFilterValue->getItemCount() > 0) + { + // Magical effects/ingredients filter + if (mFilterValue->getIndexSelected() != MyGUI::ITEM_NONE) + { + // Clear the active filter mFilterValue->clearIndexSelected(); onFilterEdited(mFilterValue); - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false); - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false); - } - else - { - if (arg.button == SDL_CONTROLLER_BUTTON_B) - { - // Remove active ingredients or close the window - if (mIngredients[3]->isUserString("ToolTipType")) - onIngredientSelected(mIngredients[3]); - else if (mIngredients[2]->isUserString("ToolTipType")) - onIngredientSelected(mIngredients[2]); - else if (mIngredients[1]->isUserString("ToolTipType")) - onIngredientSelected(mIngredients[1]); - else if (mIngredients[0]->isUserString("ToolTipType")) - onIngredientSelected(mIngredients[0]); - else - onCancelButtonClicked(mCancelButton); - } - else if (arg.button == SDL_CONTROLLER_BUTTON_X) - onCreateButtonClicked(mCreateButton); - else if (arg.button == SDL_CONTROLLER_BUTTON_Y && mFilterValue->getItemCount() > 0) - { - // Magical effects/ingredients filter - if (mFilterValue->getIndexSelected() != MyGUI::ITEM_NONE) - { - // Clear the active filter - mFilterValue->clearIndexSelected(); - onFilterEdited(mFilterValue); - } - else - { - // Open the combo box to choose the a filter - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mFilterValue); - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false); - } - MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); - } - else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) - onDecreaseButtonTriggered(); - else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) - onIncreaseButtonTriggered(); else - mItemView->onControllerButton(arg.button); + { + // Open the combo box to choose the a filter + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mFilterValue); + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false); + } + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + onDecreaseButtonTriggered(); + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + onIncreaseButtonTriggered(); + else + mItemView->onControllerButton(arg.button); return true; } diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index e79c41b659..4c5faa86d4 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -101,6 +101,7 @@ namespace MWGui std::vector mIngredients; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + void filterListButtonHandler(const SDL_ControllerButtonEvent& arg); }; } From 5b5ed21f20e4a315f1e9d09c737278aaed91618f Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 25 Jul 2025 22:53:39 -0700 Subject: [PATCH 046/102] Split journal's onControllerButtonEvent into a few functions --- apps/openmw/mwgui/journalwindow.cpp | 306 +++++++++++++--------------- apps/openmw/mwgui/journalwindow.hpp | 2 + 2 files changed, 142 insertions(+), 166 deletions(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 90db26a519..1ce9f8b024 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -712,25 +712,57 @@ namespace mTopicIndexBook->setColour(col, row, 0, focused ? MWGui::journalHeaderColour : MyGUI::Colour::Black); } - bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override + void moveSelectedIndex(int offset) { - bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); + setIndexControllerFocus(false); + int numChars = mEncoding == ToUTF8::WINDOWS_1251 ? 30 : 26; + int col = mSelectedIndex / mIndexRowCount; + + if (offset == -1) // Up + { + if (mSelectedIndex % mIndexRowCount == 0) + mSelectedIndex = (col * mIndexRowCount) + mIndexRowCount - 1; + else + mSelectedIndex--; + } + else if (offset == 1) // Down + { + if (mSelectedIndex % mIndexRowCount == mIndexRowCount - 1) + mSelectedIndex = col * mIndexRowCount; + else + mSelectedIndex++; + } + else + { + // mSelectedIndex is unsigned, so we have to be careful with our math. + if (offset < 0) + offset += numChars; + + mSelectedIndex = (mSelectedIndex + offset) % numChars; + } + + setIndexControllerFocus(true); + setText(PageOneNum, 1); // Redraw the list + } + + bool optionsModeButtonHandler(const SDL_ControllerButtonEvent& arg) + { if (arg.button == SDL_CONTROLLER_BUTTON_A) // A: Mouse click or Select { - if (mOptionsMode && mQuestMode) + if (mQuestMode) { // Choose a quest Gui::MWList* list = getWidget(QuestsList); notifyQuestClicked(list->getItemNameAt(mSelectedQuest), 0); } - else if (mOptionsMode && mTopicsMode) + else if (mTopicsMode) { // Choose a topic Gui::MWList* list = getWidget(TopicsList); notifyTopicSelected(list->getItemNameAt(mSelectedQuest), 0); } - else if (mOptionsMode) + else { // Choose an index. Cyrillic capital A is a 0xd090 in UTF-8. // Words can not be started with characters 26 or 28. @@ -739,19 +771,105 @@ namespace russianOffset++; if (mSelectedIndex >= 27) russianOffset++; // 27, not 28, because of skipping char 26 + bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); notifyIndexLinkClicked(isRussian ? mSelectedIndex + russianOffset : mSelectedIndex + 'A'); } - return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_B) // B: Back { - if (mOptionsMode) + // Hide the options overlay + notifyCancel(getWidget(CancelBTN)); + mQuestMode = false; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_X) // X: Quests + { + if (mQuestMode) { - // Hide the options overlay + // Hide the quest overlay if visible notifyCancel(getWidget(CancelBTN)); mQuestMode = false; } - else if (mStates.size() > 1) + else + { + // Show the quest overlay if viewing the topics list + notifyQuests(getWidget(QuestsBTN)); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_Y) // Y: Topics + { + if (!mQuestMode) + { + // Hide the topics overlay if visible + notifyCancel(getWidget(CancelBTN)); + } + else + { + // Show the topics overlay if viewing the quest list + notifyTopics(getWidget(TopicsBTN)); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK && mQuestMode) // R3: Show All/Some + { + if (mAllQuests) + notifyShowActive(getWidget(ShowActiveBTN)); + else + notifyShowAll(getWidget(ShowAllBTN)); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + if (mQuestMode || mTopicsMode) + { + if (mButtons.size() <= 1) + return true; + + // Scroll through the list of quests or topics + setControllerFocusedQuest(MWGui::wrap(mSelectedQuest - 1, mButtons.size())); + } + else + moveSelectedIndex(-1); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (mQuestMode || mTopicsMode) + { + if (mButtons.size() <= 1) + return true; + + // Scroll through the list of quests or topics + setControllerFocusedQuest(MWGui::wrap(mSelectedQuest + 1, mButtons.size())); + } + else + moveSelectedIndex(1); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && !mQuestMode && !mTopicsMode) + moveSelectedIndex(-mIndexRowCount); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mQuestMode && !mTopicsMode) + moveSelectedIndex(mIndexRowCount); + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER && (mQuestMode || mTopicsMode)) + { + // Scroll up 5 items in the list of quests or topics + setControllerFocusedQuest(std::max(static_cast(mSelectedQuest) - 5, 0)); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER && (mQuestMode || mTopicsMode)) + { + // Scroll down 5 items in the list of quests or topics + setControllerFocusedQuest(std::min(mSelectedQuest + 5, mButtons.size() - 1)); + } + + return true; + } + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override + { + // If the topics or quest list is open, it should handle the buttons. + if (mOptionsMode) + return optionsModeButtonHandler(arg); + + if (arg.button == SDL_CONTROLLER_BUTTON_A) + return false; + else if (arg.button == SDL_CONTROLLER_BUTTON_B) // B: Back + { + if (mStates.size() > 1) { // Pop the current book. If in quest mode, reopen the quest list. notifyJournal(getWidget(JournalBTN)); @@ -766,171 +884,27 @@ namespace // Close the journal window notifyClose(getWidget(CloseBTN)); } - return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_X) // X: Quests { - if (mOptionsMode && mQuestMode) - { - // Hide the quest overlay if visible - notifyCancel(getWidget(CancelBTN)); - mQuestMode = false; - } - else - { - // Show the quest overlay if viewing a journal entry or the topics - if (!mOptionsMode) - notifyOptions(getWidget(OptionsBTN)); - if (!mQuestMode) - notifyQuests(getWidget(QuestsBTN)); - } - return true; + // Show the quest overlay + notifyOptions(getWidget(OptionsBTN)); + if (!mQuestMode) + notifyQuests(getWidget(QuestsBTN)); } else if (arg.button == SDL_CONTROLLER_BUTTON_Y) // Y: Topics { - if (mOptionsMode && !mQuestMode) - { - // Hide the topics overlay if visible - notifyCancel(getWidget(CancelBTN)); - mQuestMode = false; - } - else - { - // Show the topics overlay if viewing a journal entry or the quest list - if (!mOptionsMode) - notifyOptions(getWidget(OptionsBTN)); - if (mQuestMode) - notifyTopics(getWidget(TopicsBTN)); - } - return true; + // Show the topics overlay + notifyOptions(getWidget(OptionsBTN)); + if (mQuestMode) + notifyTopics(getWidget(TopicsBTN)); } - else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) // R3: Show All/Some - { - if (mAllQuests) - notifyShowActive(getWidget(ShowActiveBTN)); - else - notifyShowAll(getWidget(ShowAllBTN)); - } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) - { - if (mOptionsMode && (mQuestMode || mTopicsMode)) - { - if (mButtons.size() <= 1) - return true; + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + notifyPrevPage(getWidget(PrevPageBTN)); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT || arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + notifyNextPage(getWidget(NextPageBTN)); - // Scroll through the list of quests or topics - setControllerFocusedQuest(MWGui::wrap(mSelectedQuest - 1, mButtons.size())); - } - else if (mOptionsMode) - { - setIndexControllerFocus(false); - if (mSelectedIndex % mIndexRowCount == 0) - { - int col = mSelectedIndex / mIndexRowCount; - mSelectedIndex = (col * mIndexRowCount) + mIndexRowCount - 1; - } - else - mSelectedIndex--; - setIndexControllerFocus(true); - setText(PageOneNum, 1); // Redraw the list - } - return true; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) - { - if (mOptionsMode && (mQuestMode || mTopicsMode)) - { - if (mButtons.size() <= 1) - return true; - - // Scroll through the list of quests or topics - setControllerFocusedQuest(MWGui::wrap(mSelectedQuest + 1, mButtons.size())); - } - else if (mOptionsMode) - { - setIndexControllerFocus(false); - if (mSelectedIndex % mIndexRowCount == mIndexRowCount - 1) - { - int col = mSelectedIndex / mIndexRowCount; - mSelectedIndex = col * mIndexRowCount; - } - else - mSelectedIndex++; - setIndexControllerFocus(true); - setText(PageOneNum, 1); // Redraw the list - } - return true; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) - { - if (!mOptionsMode) - notifyPrevPage(getWidget(PrevPageBTN)); - else if (mOptionsMode && !mQuestMode && !mTopicsMode) - { - setIndexControllerFocus(false); - if (isRussian) - { - // Cyrillic = 30 (10 + 10 + 10) - if (mIndexRowCount == 10) - mSelectedIndex = (mSelectedIndex + 20) % 30; - // or Cyrillic = 30 (15 + 15) - else - mSelectedIndex = (mSelectedIndex + 15) % 30; - } - else - { - // Latin = 26 (13 + 13) - mSelectedIndex = (mSelectedIndex + 13) % 26; - } - setIndexControllerFocus(true); - setText(PageOneNum, 1); // Redraw the list - } - return true; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) - { - if (!mOptionsMode) - notifyNextPage(getWidget(NextPageBTN)); - else if (mOptionsMode && !mQuestMode && !mTopicsMode) - { - setIndexControllerFocus(false); - if (isRussian) - { - // Cyrillic = 30 (10 + 10 + 10) or (15 + 15) - mSelectedIndex = (mSelectedIndex + mIndexRowCount) % 30; - } - else - { - // Latin = 26 (13 + 13) - mSelectedIndex = (mSelectedIndex + 13) % 26; - } - setIndexControllerFocus(true); - setText(PageOneNum, 1); // Redraw the list - } - return true; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) // LB: Previous Page - { - // Scroll through the list of quests or topics - if (mOptionsMode && (mQuestMode || mTopicsMode)) - setControllerFocusedQuest(std::max(static_cast(mSelectedQuest) - 5, 0)); - // Page through the journal - else if (!mOptionsMode) - notifyPrevPage(getWidget(PrevPageBTN)); - return true; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) // RB: Next Page - { - // Scroll through the list of quests or topics - if (mOptionsMode && (mQuestMode || mTopicsMode)) - setControllerFocusedQuest(std::min(mSelectedQuest + 5, mButtons.size() - 1)); - // Page through the journal - else if (!mOptionsMode) - notifyNextPage(getWidget(NextPageBTN)); - return true; - } - - return false; + return true; } void setControllerFocusedQuest(size_t index) diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index ca0893fdd4..6122bf70c7 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -37,8 +37,10 @@ namespace MWGui std::vector mButtons; size_t mSelectedQuest = 0; size_t mSelectedIndex = 0; + void moveSelectedIndex(int offset); void setIndexControllerFocus(bool focused); void setControllerFocusedQuest(size_t index); + bool optionsModeButtonHandler(const SDL_ControllerButtonEvent& arg); }; } From fb19a0da91d3c5742a65f0a20a52950d6b7324e2 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 26 Jul 2025 00:16:41 -0700 Subject: [PATCH 047/102] Fix inventory menu getting messed up when resizing game window --- apps/openmw/mwgui/windowmanagerimp.cpp | 42 ++++++++++++++------------ apps/openmw/mwgui/windowmanagerimp.hpp | 2 ++ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f7093e5d44..8073db875a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -939,6 +939,24 @@ namespace MWGui setActiveControllerWindow(mode, activeIndex); } + void WindowManager::reapplyActiveControllerWindow() + { + if (!Settings::gui().mControllerMenus || mGuiModes.empty()) + return; + + const GuiMode mode = mGuiModes.back(); + int winCount = mGuiModeStates[mode].mWindows.size(); + + for (int i = 0; i < winCount; i++) + { + // Set active window last so inactive windows don't stomp on changes it makes, e.g. to tooltips. + if (i != mActiveControllerWindows[mode]) + mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(false); + } + if (winCount > 0) + mGuiModeStates[mode].mWindows[mActiveControllerWindows[mode]]->setActiveControllerWindow(true); + } + void WindowManager::setActiveControllerWindow(GuiMode mode, int activeIndex) { if (!Settings::gui().mControllerMenus) @@ -951,13 +969,7 @@ namespace MWGui activeIndex = std::clamp(activeIndex, 0, winCount - 1); mActiveControllerWindows[mode] = activeIndex; - // Set active window last so inactive windows don't stomp on changes it makes, e.g. to tooltips. - for (int i = 0; i < winCount; i++) - { - if (i != activeIndex) - mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(false); - } - mGuiModeStates[mode].mWindows[activeIndex]->setActiveControllerWindow(true); + reapplyActiveControllerWindow(); MWBase::Environment::get().getInputManager()->setGamepadGuiCursorEnabled( mGuiModeStates[mode].mWindows[activeIndex]->isGamepadCursorAllowed()); @@ -1331,6 +1343,9 @@ namespace MWGui for (const auto& window : mWindows) window->onResChange(x, y); + // Re-apply any controller-specific window changes. + reapplyActiveControllerWindow(); + // TODO: check if any windows are now off-screen and move them back if so } @@ -1478,18 +1493,7 @@ namespace MWGui if (mGuiModes.empty()) setControllerTooltip(false); else - { - // Re-apply any controller-specific window changes. - const GuiMode mode = mGuiModes.back(); - int winCount = mGuiModeStates[mode].mWindows.size(); - - for (int i = 0; i < winCount; i++) - { - if (i != mActiveControllerWindows[mode]) - mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(false); - } - mGuiModeStates[mode].mWindows[mActiveControllerWindows[mode]]->setActiveControllerWindow(true); - } + reapplyActiveControllerWindow(); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index c45966ceff..1d2ee6a6d4 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -515,6 +515,8 @@ namespace MWGui std::map mActiveControllerWindows; bool mControllerTooltip; + void reapplyActiveControllerWindow(); + std::unique_ptr mCursorManager; std::vector> mGarbageDialogs; From defd1edb6f06c8ea6e73c85665ed716ba5836dee Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 26 Jul 2025 00:59:43 -0700 Subject: [PATCH 048/102] Fix clang error and unsigned bug --- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/journalwindow.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index d0931057cd..a9aec50448 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -589,7 +589,7 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_B) { // Remove active ingredients or close the window, starting with right-most slot. - for (size_t i = mIngredients.size() - 1; i >= 0; --i) + for (int i = mIngredients.size() - 1; i >= 0; --i) { if (mIngredients[i]->isUserString("ToolTipType")) { diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 1ce9f8b024..817ed95975 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -901,7 +901,8 @@ namespace } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) notifyPrevPage(getWidget(PrevPageBTN)); - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT || arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT + || arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) notifyNextPage(getWidget(NextPageBTN)); return true; From ab03e011274ab7b229a2b88ee0edbc364e361dbe Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 26 Jul 2025 01:35:25 -0700 Subject: [PATCH 049/102] Remove dedicated 'Unequip' button and just us 'A' --- apps/openmw/mwgui/inventorywindow.cpp | 40 ++++++++++----------------- apps/openmw/mwgui/inventorywindow.hpp | 1 - 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 06490501c3..b48083ea94 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -337,8 +337,7 @@ namespace MWGui // Show a dialog to select a count of items, but not when using an item from the inventory // in controller mode. In that case, we skip the dialog and just use one item immediately. - if (count > 1 && !shift && mPendingControllerAction != ControllerAction::Use - && mPendingControllerAction != ControllerAction::Unequip) + if (count > 1 && !shift && mPendingControllerAction != ControllerAction::Use) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); std::string message = "#{sTake}"; @@ -370,18 +369,20 @@ namespace MWGui sellItem(nullptr, count); else if (mPendingControllerAction == ControllerAction::Use) { - // Drag and drop the item on the avatar to activate it. dragItem(nullptr, count); - onAvatarClicked(nullptr); // Equip or use - // Drop any remaining items back in inventory. This is needed when clicking on a - // stack of items; we only want to use the first item. - onBackgroundSelected(); - } - else if (mPendingControllerAction == ControllerAction::Unequip) - { - // Drop on inventory background to unequip - dragItem(nullptr, count); - onBackgroundSelected(); + if (item.mType == ItemStack::Type_Equipped) + { + // Drop the item on the inventory background to unequip it. + onBackgroundSelected(); + } + else + { + // Drop the item on the avatar to activate or equip it. + onAvatarClicked(nullptr); + // Drop any remaining items back in inventory. This is needed when clicking on a + // stack of items; we only want to use the first item. + onBackgroundSelected(); + } } else if (mPendingControllerAction == ControllerAction::Drop) dropItem(nullptr, count); @@ -973,21 +974,18 @@ namespace MWGui mControllerButtons.a = "#{OMWEngine:InventorySelect}"; mControllerButtons.b = "#{Interface:Close}"; mControllerButtons.x.clear(); - mControllerButtons.y.clear(); mControllerButtons.r2 = "#{sCompanionShare}"; break; case MWGui::GM_Container: mControllerButtons.a = "#{OMWEngine:InventorySelect}"; mControllerButtons.b = "#{Interface:Close}"; mControllerButtons.x = "#{sTakeAll}"; - mControllerButtons.y.clear(); mControllerButtons.r2 = "#{sContainer}"; break; case MWGui::GM_Barter: mControllerButtons.a = "#{sSell}"; mControllerButtons.b = "#{Interface:Cancel}"; mControllerButtons.x = "#{sOffer}"; - mControllerButtons.y.clear(); mControllerButtons.r2 = "#{sBarter}"; break; case MWGui::GM_Inventory: @@ -995,7 +993,6 @@ namespace MWGui mControllerButtons.a = "#{sEquip}"; mControllerButtons.b = "#{sBack}"; mControllerButtons.x = "#{sDrop}"; - mControllerButtons.y = "#{sUnequip}"; mControllerButtons.r2.clear(); break; } @@ -1045,15 +1042,6 @@ namespace MWGui tradeWindow->onControllerButtonEvent(arg); } } - else if (arg.button == SDL_CONTROLLER_BUTTON_Y) - { - if (mGuiMode == MWGui::GM_Inventory) - { - // Unequip an item. - mPendingControllerAction = ControllerAction::Unequip; - mItemView->onControllerButton(SDL_CONTROLLER_BUTTON_A); - } - } else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { if (mFilterAll->getStateSelected()) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index bbef1e2380..a40b27564a 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -137,7 +137,6 @@ namespace MWGui Transfer, Sell, Drop, - Unequip, }; ControllerAction mPendingControllerAction; From b961007345244d3d805eb8321151566f17018736 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 26 Jul 2025 09:41:26 -0700 Subject: [PATCH 050/102] Fix unsigned type warning/error on Windows --- apps/openmw/mwgui/merchantrepair.cpp | 2 +- apps/openmw/mwgui/merchantrepair.hpp | 2 +- apps/openmw/mwgui/spellbuyingwindow.cpp | 2 +- apps/openmw/mwgui/spellbuyingwindow.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 58b05cf612..adaab63a93 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -207,7 +207,7 @@ namespace MWGui // Scroll the list to keep the active item in view if (mControllerFocus < mButtons.size()) { - int line = mButtons[mControllerFocus].second; + size_t line = mButtons[mControllerFocus].second; if (line <= 5) mList->setViewOffset(MyGUI::IntPoint(0, 0)); else diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp index 8bc9ba4bac..28fb2a7c3c 100644 --- a/apps/openmw/mwgui/merchantrepair.hpp +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -23,7 +23,7 @@ namespace MWGui MyGUI::Button* mOkButton; MyGUI::TextBox* mGoldLabel; /// List of enabled/repairable items and their index in the full list. - std::vector> mButtons; + std::vector> mButtons; MWWorld::Ptr mActor; diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 5f9914c4d9..12b4e453a6 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -267,7 +267,7 @@ namespace MWGui if (mControllerFocus < mSpellButtons.size()) { // Scroll the list to keep the active item in view - int line = mSpellButtons[mControllerFocus].second; + size_t line = mSpellButtons[mControllerFocus].second; if (line <= 5) mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0)); else diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 7a75afbed1..a53b57cc09 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -40,7 +40,7 @@ namespace MWGui std::map mSpellsWidgetMap; /// List of enabled/purchasable spells and their index in the full list. - std::vector> mSpellButtons; + std::vector> mSpellButtons; void onCancelButtonClicked(MyGUI::Widget* _sender); void onSpellButtonClick(MyGUI::Widget* _sender); From 94b460389b0c44525c2f4d4ce81abceb9941fd27 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 26 Jul 2025 22:48:35 -0700 Subject: [PATCH 051/102] Replace many includes with forward declarations --- apps/openmw/mwbase/inputmanager.hpp | 6 +++++- apps/openmw/mwbase/windowmanager.hpp | 5 +++-- apps/openmw/mwgui/companionwindow.hpp | 4 +--- apps/openmw/mwgui/countdialog.cpp | 1 + apps/openmw/mwgui/countdialog.hpp | 3 +-- apps/openmw/mwgui/inventorytabsoverlay.cpp | 1 + apps/openmw/mwgui/inventorytabsoverlay.hpp | 7 +++++-- apps/openmw/mwgui/itemchargeview.cpp | 1 + apps/openmw/mwgui/itemview.hpp | 1 - apps/openmw/mwgui/quickkeysmenu.cpp | 1 + apps/openmw/mwgui/race.cpp | 1 + apps/openmw/mwgui/race.hpp | 6 +++++- apps/openmw/mwgui/spellcreationdialog.cpp | 1 + apps/openmw/mwgui/spellcreationdialog.hpp | 3 +-- apps/openmw/mwgui/spellview.cpp | 1 + apps/openmw/mwgui/spellview.hpp | 8 +++++--- apps/openmw/mwgui/waitdialog.cpp | 1 + apps/openmw/mwgui/waitdialog.hpp | 6 +++++- apps/openmw/mwgui/windowbase.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++-- 21 files changed, 44 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 373182fb5b..2861ab88e9 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -20,6 +19,11 @@ namespace ESM class ESMWriter; } +namespace MyGUI +{ + class Widget; +} + namespace MWBase { /// \brief Interface for input manager (implemented in MWInput) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index b7380e6442..a610a66c57 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -11,10 +11,9 @@ #include -#include "../mwgui/hud.hpp" #include "../mwgui/mode.hpp" -#include "../mwgui/windowbase.hpp" +#include #include namespace ESM @@ -80,6 +79,8 @@ namespace MWGui class MessageBox; class PostProcessorHud; class SettingsWindow; + class HUD; + class WindowBase; enum ShowInDialogueMode { diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 986b75255c..1b5a772684 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -1,8 +1,6 @@ #ifndef OPENMW_MWGUI_COMPANIONWINDOW_H #define OPENMW_MWGUI_COMPANIONWINDOW_H -#include "companionitemmodel.hpp" -#include "itemmodel.hpp" #include "referenceinterface.hpp" #include "windowbase.hpp" @@ -46,7 +44,7 @@ namespace MWGui void setActiveControllerWindow(bool active) override; MWGui::ItemView* getItemView() { return mItemView; } - ItemModel* getModel() { return mModel; } + CompanionItemModel* getModel() { return mModel; } private: ItemView* mItemView; diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index db34b082e0..43c5bfe5da 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index e31c2419b8..8f0bbc7caf 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -3,11 +3,10 @@ #include "windowbase.hpp" -#include - namespace Gui { class NumericEditBox; + class ScrollBar; } namespace MWGui diff --git a/apps/openmw/mwgui/inventorytabsoverlay.cpp b/apps/openmw/mwgui/inventorytabsoverlay.cpp index fb9c7dec3d..7464a3b1ec 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.cpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.cpp @@ -1,5 +1,6 @@ #include "inventorytabsoverlay.hpp" +#include #include #include diff --git a/apps/openmw/mwgui/inventorytabsoverlay.hpp b/apps/openmw/mwgui/inventorytabsoverlay.hpp index 1f5cf39607..26544aa310 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.hpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.hpp @@ -1,10 +1,13 @@ #ifndef MWGUI_INVENTORYTABSSOVERLAY_H #define MWGUI_INVENTORYTABSSOVERLAY_H -#include - #include "windowbase.hpp" +namespace MyGUI +{ + class Button; +} + namespace MWGui { class InventoryTabsOverlay : public WindowBase diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index 31b29122f3..f7e7f71e3d 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -24,6 +24,7 @@ #include "itemmodel.hpp" #include "itemwidget.hpp" #include "textcolours.hpp" +#include "windowbase.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index e032fe3ec3..6243618abc 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -2,7 +2,6 @@ #define MWGUI_ITEMVIEW_H #include -#include #include "itemmodel.hpp" diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 23601c1b53..8aacc6f148 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index f1843fb4d7..e31f31c8be 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index ead058214c..1f6400e5b2 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -3,7 +3,6 @@ #include "windowbase.hpp" #include -#include #include namespace MWRender @@ -26,6 +25,11 @@ namespace Resource class ResourceSystem; } +namespace Gui +{ + class ScrollBar; +} + namespace MWGui { class RaceDialog : public WindowModal diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 190793b77a..49a71801aa 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 2a4f5dcbb3..35e3fb2eb2 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -1,12 +1,10 @@ #ifndef MWGUI_SPELLCREATION_H #define MWGUI_SPELLCREATION_H -#include #include #include #include -#include #include "referenceinterface.hpp" #include "widgets.hpp" @@ -15,6 +13,7 @@ namespace Gui { class MWList; + class ScrollBar; } namespace MWGui diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 6ae757b7be..ee1755e706 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -14,6 +14,7 @@ #include "../mwbase/windowmanager.hpp" #include "tooltips.hpp" +#include "windowbase.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp index a2571cd822..7895b86ec8 100644 --- a/apps/openmw/mwgui/spellview.hpp +++ b/apps/openmw/mwgui/spellview.hpp @@ -5,9 +5,6 @@ #include #include -#include - -#include #include "spellmodel.hpp" @@ -16,6 +13,11 @@ namespace MyGUI class ScrollView; } +namespace Gui +{ + class SharedStateButton; +} + namespace MWGui { diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 0943863a69..0a4d4a6ffc 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 4a7ccfcd00..c3dc45587c 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -4,7 +4,11 @@ #include "timeadvancer.hpp" #include "windowbase.hpp" #include -#include + +namespace Gui +{ + class ScrollBar; +} namespace MWGui { diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index fb88af499a..e4b4636246 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_WINDOW_BASE_H #define MWGUI_WINDOW_BASE_H -#include +#include #include "layout.hpp" diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index c5804a7858..bcc67e42c1 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -83,6 +83,7 @@ #include "confirmationdialog.hpp" #include "console.hpp" #include "container.hpp" +#include "controllerbuttonsoverlay.hpp" #include "controllers.hpp" #include "countdialog.hpp" #include "cursor.hpp" @@ -91,6 +92,7 @@ #include "enchantingdialog.hpp" #include "exposedwindow.hpp" #include "hud.hpp" +#include "inventorytabsoverlay.hpp" #include "inventorywindow.hpp" #include "itemchargeview.hpp" #include "itemtransfer.hpp" diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 06360dd9f2..211ce60dca 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -24,9 +24,7 @@ #include #include "charactercreation.hpp" -#include "controllerbuttonsoverlay.hpp" #include "draganddrop.hpp" -#include "inventorytabsoverlay.hpp" #include "mapwindow.hpp" #include "messagebox.hpp" #include "settings.hpp" @@ -47,6 +45,8 @@ namespace MyGUI class Window; class UString; class ImageBox; + class ControllerButtonsOverlay; + class InventoryTabsOverlay; } namespace MWWorld From 8a2888fa006b5fca2f8ac78396214be8212848bc Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 26 Jul 2025 22:48:35 -0700 Subject: [PATCH 052/102] Replace many includes with forward declarations --- apps/openmw/mwbase/inputmanager.hpp | 6 +++++- apps/openmw/mwbase/windowmanager.hpp | 4 ++-- apps/openmw/mwgui/companionwindow.hpp | 4 +--- apps/openmw/mwgui/countdialog.cpp | 1 + apps/openmw/mwgui/countdialog.hpp | 3 +-- apps/openmw/mwgui/inventorytabsoverlay.cpp | 1 + apps/openmw/mwgui/inventorytabsoverlay.hpp | 7 +++++-- apps/openmw/mwgui/itemchargeview.cpp | 1 + apps/openmw/mwgui/itemview.hpp | 1 - apps/openmw/mwgui/quickkeysmenu.cpp | 1 + apps/openmw/mwgui/race.cpp | 1 + apps/openmw/mwgui/race.hpp | 6 +++++- apps/openmw/mwgui/spellcreationdialog.cpp | 1 + apps/openmw/mwgui/spellcreationdialog.hpp | 3 +-- apps/openmw/mwgui/spellview.cpp | 1 + apps/openmw/mwgui/spellview.hpp | 8 +++++--- apps/openmw/mwgui/textinput.cpp | 2 ++ apps/openmw/mwgui/waitdialog.cpp | 1 + apps/openmw/mwgui/waitdialog.hpp | 6 +++++- apps/openmw/mwgui/windowbase.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++-- 22 files changed, 45 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 373182fb5b..2861ab88e9 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -20,6 +19,11 @@ namespace ESM class ESMWriter; } +namespace MyGUI +{ + class Widget; +} + namespace MWBase { /// \brief Interface for input manager (implemented in MWInput) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index b7380e6442..2783fd21d5 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -11,9 +11,7 @@ #include -#include "../mwgui/hud.hpp" #include "../mwgui/mode.hpp" -#include "../mwgui/windowbase.hpp" #include @@ -80,6 +78,8 @@ namespace MWGui class MessageBox; class PostProcessorHud; class SettingsWindow; + class HUD; + class WindowBase; enum ShowInDialogueMode { diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 986b75255c..1b5a772684 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -1,8 +1,6 @@ #ifndef OPENMW_MWGUI_COMPANIONWINDOW_H #define OPENMW_MWGUI_COMPANIONWINDOW_H -#include "companionitemmodel.hpp" -#include "itemmodel.hpp" #include "referenceinterface.hpp" #include "windowbase.hpp" @@ -46,7 +44,7 @@ namespace MWGui void setActiveControllerWindow(bool active) override; MWGui::ItemView* getItemView() { return mItemView; } - ItemModel* getModel() { return mModel; } + CompanionItemModel* getModel() { return mModel; } private: ItemView* mItemView; diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index db34b082e0..43c5bfe5da 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index e31c2419b8..8f0bbc7caf 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -3,11 +3,10 @@ #include "windowbase.hpp" -#include - namespace Gui { class NumericEditBox; + class ScrollBar; } namespace MWGui diff --git a/apps/openmw/mwgui/inventorytabsoverlay.cpp b/apps/openmw/mwgui/inventorytabsoverlay.cpp index fb9c7dec3d..7464a3b1ec 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.cpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.cpp @@ -1,5 +1,6 @@ #include "inventorytabsoverlay.hpp" +#include #include #include diff --git a/apps/openmw/mwgui/inventorytabsoverlay.hpp b/apps/openmw/mwgui/inventorytabsoverlay.hpp index 1f5cf39607..26544aa310 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.hpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.hpp @@ -1,10 +1,13 @@ #ifndef MWGUI_INVENTORYTABSSOVERLAY_H #define MWGUI_INVENTORYTABSSOVERLAY_H -#include - #include "windowbase.hpp" +namespace MyGUI +{ + class Button; +} + namespace MWGui { class InventoryTabsOverlay : public WindowBase diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index 31b29122f3..f7e7f71e3d 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -24,6 +24,7 @@ #include "itemmodel.hpp" #include "itemwidget.hpp" #include "textcolours.hpp" +#include "windowbase.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index e032fe3ec3..6243618abc 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -2,7 +2,6 @@ #define MWGUI_ITEMVIEW_H #include -#include #include "itemmodel.hpp" diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 23601c1b53..8aacc6f148 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index f1843fb4d7..e31f31c8be 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index ead058214c..1f6400e5b2 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -3,7 +3,6 @@ #include "windowbase.hpp" #include -#include #include namespace MWRender @@ -26,6 +25,11 @@ namespace Resource class ResourceSystem; } +namespace Gui +{ + class ScrollBar; +} + namespace MWGui { class RaceDialog : public WindowModal diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 190793b77a..49a71801aa 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 2a4f5dcbb3..35e3fb2eb2 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -1,12 +1,10 @@ #ifndef MWGUI_SPELLCREATION_H #define MWGUI_SPELLCREATION_H -#include #include #include #include -#include #include "referenceinterface.hpp" #include "widgets.hpp" @@ -15,6 +13,7 @@ namespace Gui { class MWList; + class ScrollBar; } namespace MWGui diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 6ae757b7be..ee1755e706 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -14,6 +14,7 @@ #include "../mwbase/windowmanager.hpp" #include "tooltips.hpp" +#include "windowbase.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp index a2571cd822..7895b86ec8 100644 --- a/apps/openmw/mwgui/spellview.hpp +++ b/apps/openmw/mwgui/spellview.hpp @@ -5,9 +5,6 @@ #include #include -#include - -#include #include "spellmodel.hpp" @@ -16,6 +13,11 @@ namespace MyGUI class ScrollView; } +namespace Gui +{ + class SharedStateButton; +} + namespace MWGui { diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 9e75d0ba01..5594501c8c 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -7,6 +7,8 @@ #include #include +#include + namespace MWGui { diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 0943863a69..0a4d4a6ffc 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 4a7ccfcd00..c3dc45587c 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -4,7 +4,11 @@ #include "timeadvancer.hpp" #include "windowbase.hpp" #include -#include + +namespace Gui +{ + class ScrollBar; +} namespace MWGui { diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index fb88af499a..e4b4636246 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_WINDOW_BASE_H #define MWGUI_WINDOW_BASE_H -#include +#include #include "layout.hpp" diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index c5804a7858..bcc67e42c1 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -83,6 +83,7 @@ #include "confirmationdialog.hpp" #include "console.hpp" #include "container.hpp" +#include "controllerbuttonsoverlay.hpp" #include "controllers.hpp" #include "countdialog.hpp" #include "cursor.hpp" @@ -91,6 +92,7 @@ #include "enchantingdialog.hpp" #include "exposedwindow.hpp" #include "hud.hpp" +#include "inventorytabsoverlay.hpp" #include "inventorywindow.hpp" #include "itemchargeview.hpp" #include "itemtransfer.hpp" diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 06360dd9f2..211ce60dca 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -24,9 +24,7 @@ #include #include "charactercreation.hpp" -#include "controllerbuttonsoverlay.hpp" #include "draganddrop.hpp" -#include "inventorytabsoverlay.hpp" #include "mapwindow.hpp" #include "messagebox.hpp" #include "settings.hpp" @@ -47,6 +45,8 @@ namespace MyGUI class Window; class UString; class ImageBox; + class ControllerButtonsOverlay; + class InventoryTabsOverlay; } namespace MWWorld From 2a0ff5626c08008b154eb7df103fc6134b048691 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 26 Jul 2025 22:56:47 -0700 Subject: [PATCH 053/102] Remove extra include that was inadvertently added --- apps/openmw/mwbase/windowmanager.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index a610a66c57..2783fd21d5 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -13,7 +13,6 @@ #include "../mwgui/mode.hpp" -#include #include namespace ESM From 24c7a3f1ce8c88ce31a3e4ebf6a61dd9da9b81c3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 26 Jul 2025 23:53:57 -0700 Subject: [PATCH 054/102] Remove duplicate class declarations --- apps/openmw/mwgui/windowmanagerimp.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 211ce60dca..81294d3cd4 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -45,8 +45,6 @@ namespace MyGUI class Window; class UString; class ImageBox; - class ControllerButtonsOverlay; - class InventoryTabsOverlay; } namespace MWWorld From 360c801b73f65b60310dfda5a0a940fde9ac1b53 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 26 Jul 2025 23:54:25 -0700 Subject: [PATCH 055/102] Remove local wrapper of MyGUI::Scrollbar --- apps/openmw/mwgui/countdialog.cpp | 1 - apps/openmw/mwgui/countdialog.hpp | 3 +-- apps/openmw/mwgui/race.cpp | 1 - apps/openmw/mwgui/race.hpp | 7 +------ apps/openmw/mwgui/spellcreationdialog.cpp | 1 - apps/openmw/mwgui/spellcreationdialog.hpp | 9 ++++----- apps/openmw/mwgui/waitdialog.cpp | 1 - apps/openmw/mwgui/waitdialog.hpp | 7 +------ components/CMakeLists.txt | 2 +- components/widgets/scrollbar.cpp | 17 ----------------- components/widgets/scrollbar.hpp | 18 ------------------ components/widgets/widgets.cpp | 2 -- 12 files changed, 8 insertions(+), 61 deletions(-) delete mode 100644 components/widgets/scrollbar.cpp delete mode 100644 components/widgets/scrollbar.hpp diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 43c5bfe5da..db34b082e0 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -5,7 +5,6 @@ #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 8f0bbc7caf..0b1e53d167 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -6,7 +6,6 @@ namespace Gui { class NumericEditBox; - class ScrollBar; } namespace MWGui @@ -24,7 +23,7 @@ namespace MWGui MyGUI::delegates::MultiDelegate eventOkClicked; private: - Gui::ScrollBar* mSlider; + MyGUI::ScrollBar* mSlider; Gui::NumericEditBox* mItemEdit; MyGUI::TextBox* mItemText; MyGUI::TextBox* mLabelText; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index e31f31c8be..f1843fb4d7 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 1f6400e5b2..0b00de40c0 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -25,11 +25,6 @@ namespace Resource class ResourceSystem; } -namespace Gui -{ - class ScrollBar; -} - namespace MWGui { class RaceDialog : public WindowModal @@ -105,7 +100,7 @@ namespace MWGui MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; - Gui::ScrollBar* mHeadRotate; + MyGUI::ScrollBar* mHeadRotate; MyGUI::Button* mBackButton; MyGUI::Button* mOkButton; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 49a71801aa..190793b77a 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 35e3fb2eb2..f4624c89b3 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -13,7 +13,6 @@ namespace Gui { class MWList; - class ScrollBar; } namespace MWGui @@ -59,10 +58,10 @@ namespace MWGui MyGUI::TextBox* mDurationValue; MyGUI::TextBox* mAreaValue; - Gui::ScrollBar* mMagnitudeMinSlider; - Gui::ScrollBar* mMagnitudeMaxSlider; - Gui::ScrollBar* mDurationSlider; - Gui::ScrollBar* mAreaSlider; + MyGUI::ScrollBar* mMagnitudeMinSlider; + MyGUI::ScrollBar* mMagnitudeMaxSlider; + MyGUI::ScrollBar* mDurationSlider; + MyGUI::ScrollBar* mAreaSlider; MyGUI::TextBox* mAreaText; diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 0a4d4a6ffc..0943863a69 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index c3dc45587c..19d7752946 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -5,11 +5,6 @@ #include "windowbase.hpp" #include -namespace Gui -{ - class ScrollBar; -} - namespace MWGui { @@ -60,7 +55,7 @@ namespace MWGui MyGUI::Button* mUntilHealedButton; MyGUI::Button* mWaitButton; MyGUI::Button* mCancelButton; - Gui::ScrollBar* mHourSlider; + MyGUI::ScrollBar* mHourSlider; TimeAdvancer mTimeAdvancer; bool mSleeping; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 184da4da25..8a3325ea71 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -354,7 +354,7 @@ add_component_dir (myguiplatform ) add_component_dir (widgets - box fontwrapper imagebutton tags list numericeditbox scrollbar sharedstatebutton windowcaption widgets + box fontwrapper imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets ) add_component_dir (fontloader diff --git a/components/widgets/scrollbar.cpp b/components/widgets/scrollbar.cpp deleted file mode 100644 index 0384003e65..0000000000 --- a/components/widgets/scrollbar.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "scrollbar.hpp" - -#include - -namespace Gui -{ - std::vector ScrollBar::getAllWidgets() - { - std::vector widgets; - widgets.push_back(mWidgetStart); - widgets.push_back(mWidgetEnd); - widgets.push_back(mWidgetTrack); - widgets.push_back(mWidgetFirstPart); - widgets.push_back(mWidgetSecondPart); - return widgets; - } -} diff --git a/components/widgets/scrollbar.hpp b/components/widgets/scrollbar.hpp deleted file mode 100644 index fa4cee01f0..0000000000 --- a/components/widgets/scrollbar.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef OPENMW_COMPONENTS_WIDGETS_SCROLLBAR_H -#define OPENMW_COMPONENTS_WIDGETS_SCROLLBAR_H - -#include - -namespace Gui -{ - /// @brief A scrollbar that can return all its widgets for binding hover listeners. - class ScrollBar : public MyGUI::ScrollBar - { - MYGUI_RTTI_DERIVED(ScrollBar) - - public: - std::vector getAllWidgets(); - }; -} - -#endif diff --git a/components/widgets/widgets.cpp b/components/widgets/widgets.cpp index 58b5736b30..d27d7e5fc9 100644 --- a/components/widgets/widgets.cpp +++ b/components/widgets/widgets.cpp @@ -6,7 +6,6 @@ #include "imagebutton.hpp" #include "list.hpp" #include "numericeditbox.hpp" -#include "scrollbar.hpp" #include "sharedstatebutton.hpp" #include "windowcaption.hpp" @@ -27,7 +26,6 @@ namespace Gui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } From 984582b5865022a11c77e797e839283434146f37 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 27 Jul 2025 00:06:05 -0700 Subject: [PATCH 056/102] Cleanup includes for itemchargeview --- apps/openmw/mwgui/itemchargeview.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index f7e7f71e3d..88b3869fbc 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -1,19 +1,15 @@ #include "itemchargeview.hpp" -#include +#include #include #include #include #include -#include -#include -#include #include #include "../mwbase/environment.hpp" -#include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/spellutil.hpp" From de057cc03065897af32da9963262543021e51745 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 27 Jul 2025 00:33:57 -0700 Subject: [PATCH 057/102] Update includes on controllermanager --- apps/openmw/mwinput/controllermanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 7965a18f46..f34d22227f 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -16,7 +16,7 @@ #include "../mwbase/luamanager.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwgui/inventorywindow.hpp" +#include "../mwgui/windowbase.hpp" #include "actions.hpp" #include "bindingsmanager.hpp" From 3c3f36679e7b01ca9e9c789a3ac9e5ba832985fd Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 27 Jul 2025 00:45:54 -0700 Subject: [PATCH 058/102] Re-add ESM include --- apps/openmw/mwgui/itemchargeview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index 88b3869fbc..59731717e1 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "../mwbase/environment.hpp" From 2d5ec4889226933c12cd02e50aa487ef9cd1f191 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 27 Jul 2025 12:56:56 -0700 Subject: [PATCH 059/102] Fix default return type for getControllerAxisIcon --- apps/openmw/mwinput/controllermanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index f34d22227f..3b352decfc 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -557,7 +557,7 @@ namespace MWInput return "textures/omw_switch_button_zr.dds"; return "textures/omw_steam_button_r2.dds"; default: - return ""; + return {}; } } From 9870605aab49644e1bafaf85a4e812e4cc89479e Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 27 Jul 2025 12:57:56 -0700 Subject: [PATCH 060/102] Update enum defintion for ControllerAction --- apps/openmw/mwgui/inventorywindow.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index a40b27564a..343fe6999c 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -130,9 +130,9 @@ namespace MWGui void onBackgroundSelected(); - enum ControllerAction + enum class ControllerAction { - None = 0, + None, Use, Transfer, Sell, From dc2ff4bfa20bd5520c00e21fae56ca97095ea011 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 27 Jul 2025 13:14:31 -0700 Subject: [PATCH 061/102] Rename ControllerButtonStr struct and its internal fields --- apps/openmw/mwgui/alchemywindow.cpp | 10 ++--- apps/openmw/mwgui/birth.cpp | 10 ++--- apps/openmw/mwgui/bookwindow.cpp | 10 ++--- apps/openmw/mwgui/bookwindow.hpp | 2 +- apps/openmw/mwgui/class.cpp | 40 +++++++++---------- apps/openmw/mwgui/companionwindow.cpp | 8 ++-- apps/openmw/mwgui/confirmationdialog.cpp | 4 +- apps/openmw/mwgui/container.cpp | 14 +++---- apps/openmw/mwgui/container.hpp | 2 +- .../openmw/mwgui/controllerbuttonsoverlay.cpp | 32 +++++++-------- .../openmw/mwgui/controllerbuttonsoverlay.hpp | 2 +- apps/openmw/mwgui/countdialog.cpp | 4 +- apps/openmw/mwgui/dialogue.cpp | 10 ++--- apps/openmw/mwgui/enchantingdialog.cpp | 14 +++---- apps/openmw/mwgui/inventorywindow.cpp | 36 ++++++++--------- apps/openmw/mwgui/inventorywindow.hpp | 2 +- apps/openmw/mwgui/itemselection.cpp | 6 +-- apps/openmw/mwgui/journalwindow.cpp | 16 ++++---- apps/openmw/mwgui/levelupdialog.cpp | 4 +- apps/openmw/mwgui/mapwindow.cpp | 18 ++++----- apps/openmw/mwgui/mapwindow.hpp | 2 +- apps/openmw/mwgui/merchantrepair.cpp | 4 +- apps/openmw/mwgui/messagebox.cpp | 2 +- apps/openmw/mwgui/quickkeysmenu.cpp | 12 +++--- apps/openmw/mwgui/race.cpp | 16 ++++---- apps/openmw/mwgui/recharge.cpp | 6 +-- apps/openmw/mwgui/repair.cpp | 6 +-- apps/openmw/mwgui/review.cpp | 6 +-- apps/openmw/mwgui/savegamedialog.cpp | 8 ++-- apps/openmw/mwgui/savegamedialog.hpp | 2 +- apps/openmw/mwgui/scrollwindow.cpp | 8 ++-- apps/openmw/mwgui/scrollwindow.hpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 6 +-- apps/openmw/mwgui/spellbuyingwindow.cpp | 6 +-- apps/openmw/mwgui/spellcreationdialog.cpp | 14 +++---- apps/openmw/mwgui/spellwindow.cpp | 6 +-- apps/openmw/mwgui/statswindow.cpp | 6 +-- apps/openmw/mwgui/textinput.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 10 ++--- apps/openmw/mwgui/trainingwindow.cpp | 4 +- apps/openmw/mwgui/travelwindow.cpp | 4 +- apps/openmw/mwgui/waitdialog.cpp | 8 ++-- apps/openmw/mwgui/waitdialog.hpp | 2 +- apps/openmw/mwgui/windowbase.hpp | 36 ++++++++--------- 44 files changed, 211 insertions(+), 211 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index a9aec50448..66461e6e6a 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -94,11 +94,11 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.x = "#{sCreate}"; - mControllerButtons.y = "#{sMagicEffects}"; - mControllerButtons.r3 = "#{sInfo}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mX = "#{sCreate}"; + mControllerButtons.mY = "#{sMagicEffects}"; + mControllerButtons.mR3 = "#{sInfo}"; } center(); diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 5a5605aa6e..97405e7cc0 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -60,9 +60,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.lStick = "#{sMouse}"; - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.mLStick = "#{sMouse}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{sBack}"; } updateBirths(); @@ -78,13 +78,13 @@ namespace MWGui { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {}))); - mControllerButtons.x = "#{sNext}"; + mControllerButtons.mX = "#{sNext}"; } else if (Settings::gui().mControllerMenus) { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); - mControllerButtons.x = "#{sDone}"; + mControllerButtons.mX = "#{sDone}"; } else okButton->setCaption( diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 0ad31680b7..056f1abc89 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -66,9 +66,9 @@ namespace MWGui MyGUI::IntCoord(0, 0, (64 - 7) * scale, mNextPageButton->getSize().height * scale)); } - mControllerButtons.l1 = "#{sPrev}"; - mControllerButtons.r1 = "#{sNext}"; - mControllerButtons.b = "#{Interface:Close}"; + mControllerButtons.mL1 = "#{sPrev}"; + mControllerButtons.mR1 = "#{sNext}"; + mControllerButtons.mB = "#{Interface:Close}"; center(); } @@ -222,9 +222,9 @@ namespace MWGui } } - ControllerButtonStr* BookWindow::getControllerButtons() + ControllerButtons* BookWindow::getControllerButtons() { - mControllerButtons.a = mTakeButton->getVisible() ? "#{sTake}" : ""; + mControllerButtons.mA = mTakeButton->getVisible() ? "#{sTake}" : ""; return &mControllerButtons; } diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index 0a1beb7342..7d3a0e30c7 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -21,7 +21,7 @@ namespace MWGui bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; std::string_view getWindowIdForLua() const override { return "Book"; } - ControllerButtonStr* getControllerButtons() override; + ControllerButtons* getControllerButtons() override; protected: void onNextPageButtonClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index b0dbc6c779..efd2872408 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -59,8 +59,8 @@ namespace MWGui { mOkButton->setStateSelected(true); mDisableGamepadCursor = true; - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{sBack}"; } center(); @@ -149,9 +149,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.lStick = "#{sMouse}"; - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.mLStick = "#{sMouse}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{sBack}"; } updateClasses(); @@ -167,13 +167,13 @@ namespace MWGui { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {}))); - mControllerButtons.x = "#{sNext}"; + mControllerButtons.mX = "#{sNext}"; } else if (Settings::gui().mControllerMenus) { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); - mControllerButtons.x = "#{sDone}"; + mControllerButtons.mX = "#{sDone}"; } else okButton->setCaption( @@ -393,7 +393,7 @@ namespace MWGui center(); mDisableGamepadCursor = Settings::gui().mControllerMenus; - mControllerButtons.a = "#{sSelect}"; + mControllerButtons.mA = "#{sSelect}"; } void InfoBoxDialog::setText(const std::string& str) @@ -586,9 +586,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { okButton->setStateSelected(true); - mControllerButtons.lStick = "#{sMouse}"; - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.mLStick = "#{sMouse}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{sBack}"; } // Set default skills, attributes @@ -680,13 +680,13 @@ namespace MWGui { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {}))); - mControllerButtons.x = "#{sNext}"; + mControllerButtons.mX = "#{sNext}"; } else if (Settings::gui().mControllerMenus) { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); - mControllerButtons.x = "#{sDone}"; + mControllerButtons.mX = "#{sDone}"; } else okButton->setCaption( @@ -884,8 +884,8 @@ namespace MWGui getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } SelectSpecializationDialog::~SelectSpecializationDialog() {} @@ -966,8 +966,8 @@ namespace MWGui if (mAttributeButtons.size() > 0) mAttributeButtons[0]->setStateSelected(true); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } } @@ -1066,8 +1066,8 @@ namespace MWGui if (mSkillButtons.size() > 0) mSkillButtons[0]->setStateSelected(true); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } } @@ -1156,7 +1156,7 @@ namespace MWGui // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); - mControllerButtons.a = "#{Interface:OK}"; + mControllerButtons.mA = "#{Interface:OK}"; } DescriptionDialog::~DescriptionDialog() {} diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index fcbaf146ec..0972f8e57e 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -64,10 +64,10 @@ namespace MWGui setCoord(200, 0, 600, 300); - mControllerButtons.a = "#{sTake}"; - mControllerButtons.b = "#{Interface:Close}"; - mControllerButtons.r3 = "#{sInfo}"; - mControllerButtons.l2 = "#{sInventory}"; + mControllerButtons.mA = "#{sTake}"; + mControllerButtons.mB = "#{Interface:Close}"; + mControllerButtons.mR3 = "#{sInfo}"; + mControllerButtons.mL2 = "#{sInventory}"; } void CompanionWindow::onItemSelected(int index) diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index b0a0f62580..f23237c896 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -23,8 +23,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - mControllerButtons.a = "#{Interface:OK}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{Interface:OK}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 84fd945e79..126b6ea1d3 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -59,11 +59,11 @@ namespace MWGui setCoord(200, 0, 600, 300); - mControllerButtons.a = "#{sTake}"; - mControllerButtons.b = "#{Interface:Close}"; - mControllerButtons.x = "#{sTakeAll}"; - mControllerButtons.r3 = "#{sInfo}"; - mControllerButtons.l2 = "#{sInventory}"; + mControllerButtons.mA = "#{sTake}"; + mControllerButtons.mB = "#{Interface:Close}"; + mControllerButtons.mX = "#{sTakeAll}"; + mControllerButtons.mR3 = "#{sInfo}"; + mControllerButtons.mL2 = "#{sInventory}"; } void ContainerWindow::onItemSelected(int index) @@ -353,9 +353,9 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } - ControllerButtonStr* ContainerWindow::getControllerButtons() + ControllerButtons* ContainerWindow::getControllerButtons() { - mControllerButtons.r1 = mDisposeCorpseButton->getVisible() ? "#{sDisposeofCorpse}" : ""; + mControllerButtons.mR1 = mDisposeCorpseButton->getVisible() ? "#{sDisposeofCorpse}" : ""; return &mControllerButtons; } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 7231445c8f..d40507bd82 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -48,7 +48,7 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Container"; } - ControllerButtonStr* getControllerButtons() override; + ControllerButtons* getControllerButtons() override; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; void setActiveControllerWindow(bool active) override; diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index ac4f95ff7c..fc4cdfaec2 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -46,24 +46,24 @@ namespace MWGui return window->getHeight(); } - void ControllerButtonsOverlay::setButtons(ControllerButtonStr* buttons) + void ControllerButtonsOverlay::setButtons(ControllerButtons* buttons) { int buttonCount = 0; - buttonCount += updateButton(Button::Button_A, buttons->a); - buttonCount += updateButton(Button::Button_B, buttons->b); - buttonCount += updateButton(Button::Button_Dpad, buttons->dpad); - buttonCount += updateButton(Button::Button_L1, buttons->l1); - buttonCount += updateButton(Button::Button_L2, buttons->l2); - buttonCount += updateButton(Button::Button_L3, buttons->l3); - buttonCount += updateButton(Button::Button_LStick, buttons->lStick); - buttonCount += updateButton(Button::Button_Menu, buttons->menu); - buttonCount += updateButton(Button::Button_R1, buttons->r1); - buttonCount += updateButton(Button::Button_R2, buttons->r2); - buttonCount += updateButton(Button::Button_R3, buttons->r3); - buttonCount += updateButton(Button::Button_RStick, buttons->rStick); - buttonCount += updateButton(Button::Button_View, buttons->view); - buttonCount += updateButton(Button::Button_X, buttons->x); - buttonCount += updateButton(Button::Button_Y, buttons->y); + buttonCount += updateButton(Button::Button_A, buttons->mA); + buttonCount += updateButton(Button::Button_B, buttons->mB); + buttonCount += updateButton(Button::Button_Dpad, buttons->mDpad); + buttonCount += updateButton(Button::Button_L1, buttons->mL1); + buttonCount += updateButton(Button::Button_L2, buttons->mL2); + buttonCount += updateButton(Button::Button_L3, buttons->mL3); + buttonCount += updateButton(Button::Button_LStick, buttons->mLStick); + buttonCount += updateButton(Button::Button_Menu, buttons->mMenu); + buttonCount += updateButton(Button::Button_R1, buttons->mR1); + buttonCount += updateButton(Button::Button_R2, buttons->mR2); + buttonCount += updateButton(Button::Button_R3, buttons->mR3); + buttonCount += updateButton(Button::Button_RStick, buttons->mRStick); + buttonCount += updateButton(Button::Button_View, buttons->mView); + buttonCount += updateButton(Button::Button_X, buttons->mX); + buttonCount += updateButton(Button::Button_Y, buttons->mY); mHBox->notifyChildrenSizeChanged(); diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index 623424caf2..0c7d484e6c 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -16,7 +16,7 @@ namespace MWGui ControllerButtonsOverlay(); int getHeight(); - void setButtons(ControllerButtonStr* buttons); + void setButtons(ControllerButtons* buttons); private: enum Button diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index db34b082e0..0214d34e8a 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -28,8 +28,8 @@ namespace MWGui // make sure we read the enter key being pressed to accept multiple items mItemEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &CountDialog::onEnterKeyPressed); - mControllerButtons.a = "#{Interface:OK}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{Interface:OK}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } void CountDialog::openCountDialog(const std::string& item, const std::string& message, const int maxCount) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 587c4d03c5..ec7d726570 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -90,8 +90,8 @@ namespace MWGui mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mDisableGamepadCursor = Settings::gui().mControllerMenus; - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } void PersuasionDialog::adjustAction(MyGUI::Widget* action, int& totalHeight) @@ -387,9 +387,9 @@ namespace MWGui += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); mControllerScrollWidget = mHistory->getParent(); - mControllerButtons.a = "#{sAsk}"; - mControllerButtons.b = "#{sGoodbye}"; - mControllerButtons.rStick = "#{sScrollup}"; + mControllerButtons.mA = "#{sAsk}"; + mControllerButtons.mB = "#{sGoodbye}"; + mControllerButtons.mRStick = "#{sScrollup}"; } void DialogueWindow::onTradeComplete() diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 056862fdce..d6a42f8ea0 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -60,11 +60,11 @@ namespace MWGui mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked); mName->eventEditSelectAccept += MyGUI::newDelegate(this, &EnchantingDialog::onAccept); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.y = "#{OMWEngine:EnchantType}"; - mControllerButtons.l1 = "#{sItem}"; - mControllerButtons.r1 = "#{sSoulGem}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mY = "#{OMWEngine:EnchantType}"; + mControllerButtons.mL1 = "#{sItem}"; + mControllerButtons.mR1 = "#{sSoulGem}"; } void EnchantingDialog::onOpen() @@ -158,7 +158,7 @@ namespace MWGui mEnchanting.setSelfEnchanting(false); mEnchanting.setEnchanter(ptr); mBuyButton->setCaptionWithReplacing("#{sBuy}"); - mControllerButtons.x = "#{sBuy}"; + mControllerButtons.mX = "#{sBuy}"; mChanceLayout->setVisible(false); mPtr = ptr; setSoulGem(MWWorld::Ptr()); @@ -170,7 +170,7 @@ namespace MWGui mEnchanting.setSelfEnchanting(true); mEnchanting.setEnchanter(MWMechanics::getPlayer()); mBuyButton->setCaptionWithReplacing("#{sCreate}"); - mControllerButtons.x = "#{sCreate}"; + mControllerButtons.mX = "#{sCreate}"; mChanceLayout->setVisible(Settings::game().mShowEnchantChance); mPtr = MWMechanics::getPlayer(); setSoulGem(ptr); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index b48083ea94..6336abd3d5 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -151,7 +151,7 @@ namespace MWGui image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon( SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); - mControllerButtons.r3 = "#{sInfo}"; + mControllerButtons.mR3 = "#{sInfo}"; } adjustPanes(); @@ -966,34 +966,34 @@ namespace MWGui return osg::Vec2f(normalisedX * float(viewport.width - 1), (1.0 - normalisedY) * float(viewport.height - 1)); } - ControllerButtonStr* InventoryWindow::getControllerButtons() + ControllerButtons* InventoryWindow::getControllerButtons() { switch (mGuiMode) { case MWGui::GM_Companion: - mControllerButtons.a = "#{OMWEngine:InventorySelect}"; - mControllerButtons.b = "#{Interface:Close}"; - mControllerButtons.x.clear(); - mControllerButtons.r2 = "#{sCompanionShare}"; + mControllerButtons.mA = "#{OMWEngine:InventorySelect}"; + mControllerButtons.mB = "#{Interface:Close}"; + mControllerButtons.mX.clear(); + mControllerButtons.mR2 = "#{sCompanionShare}"; break; case MWGui::GM_Container: - mControllerButtons.a = "#{OMWEngine:InventorySelect}"; - mControllerButtons.b = "#{Interface:Close}"; - mControllerButtons.x = "#{sTakeAll}"; - mControllerButtons.r2 = "#{sContainer}"; + mControllerButtons.mA = "#{OMWEngine:InventorySelect}"; + mControllerButtons.mB = "#{Interface:Close}"; + mControllerButtons.mX = "#{sTakeAll}"; + mControllerButtons.mR2 = "#{sContainer}"; break; case MWGui::GM_Barter: - mControllerButtons.a = "#{sSell}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.x = "#{sOffer}"; - mControllerButtons.r2 = "#{sBarter}"; + mControllerButtons.mA = "#{sSell}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mX = "#{sOffer}"; + mControllerButtons.mR2 = "#{sBarter}"; break; case MWGui::GM_Inventory: default: - mControllerButtons.a = "#{sEquip}"; - mControllerButtons.b = "#{sBack}"; - mControllerButtons.x = "#{sDrop}"; - mControllerButtons.r2.clear(); + mControllerButtons.mA = "#{sEquip}"; + mControllerButtons.mB = "#{sBack}"; + mControllerButtons.mX = "#{sDrop}"; + mControllerButtons.mR2.clear(); break; } return &mControllerButtons; diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 343fe6999c..ce02a83c1b 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -77,7 +77,7 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Inventory"; } - ControllerButtonStr* getControllerButtons() override; + ControllerButtons* getControllerButtons() override; protected: void onTitleDoubleClicked() override; diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 8dc806fdd2..30d8dbfb60 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -29,9 +29,9 @@ namespace MWGui center(); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.r3 = "#{sInfo}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mR3 = "#{sInfo}"; } bool ItemSelectionDialog::exit() diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 817ed95975..0d8e8cf4ef 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -224,9 +224,9 @@ namespace if (isRussian) // Cyrillic is either (10 + 10 + 10) or (15 + 15) mIndexRowCount = MWGui::getCyrillicIndexPageCount(); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.x = "#{OMWEngine:JournalQuests}"; - mControllerButtons.y = "#{sTopics}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mX = "#{OMWEngine:JournalQuests}"; + mControllerButtons.mY = "#{sTopics}"; mQuestMode = false; mAllQuests = false; @@ -696,12 +696,12 @@ namespace } } - MWGui::ControllerButtonStr* getControllerButtons() override + MWGui::ControllerButtons* getControllerButtons() override { - mControllerButtons.b = mOptionsMode || mStates.size() > 1 ? "#{sBack}" : "#{Interface:Close}"; - mControllerButtons.l1 = mOptionsMode ? "" : "#{sPrev}"; - mControllerButtons.r1 = mOptionsMode ? "" : "#{sNext}"; - mControllerButtons.r3 = mOptionsMode && mQuestMode ? "#{OMWEngine:JournalShowAll}" : ""; + mControllerButtons.mB = mOptionsMode || mStates.size() > 1 ? "#{sBack}" : "#{Interface:Close}"; + mControllerButtons.mL1 = mOptionsMode ? "" : "#{sPrev}"; + mControllerButtons.mR1 = mOptionsMode ? "" : "#{sNext}"; + mControllerButtons.mR3 = mOptionsMode && mQuestMode ? "#{OMWEngine:JournalShowAll}" : ""; return &mControllerButtons; } diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 34f70e8660..6186401d0a 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -95,8 +95,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.x = "#{sDone}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mX = "#{sDone}"; mOkButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 7dc1fdaf15..06bf8ed50d 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -836,10 +836,10 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.b = "#{sBack}"; - mControllerButtons.x = global ? "#{sLocal}" : "#{sWorld}"; - mControllerButtons.y = "#{sCenter}"; - mControllerButtons.dpad = Settings::map().mAllowZooming ? "" : "#{sMove}"; + mControllerButtons.mB = "#{sBack}"; + mControllerButtons.mX = global ? "#{sLocal}" : "#{sWorld}"; + mControllerButtons.mY = "#{sCenter}"; + mControllerButtons.mDpad = Settings::map().mAllowZooming ? "" : "#{sMove}"; } } @@ -1230,7 +1230,7 @@ namespace MWGui mLocalMap->setVisible(!global); mButton->setCaptionWithReplacing(global ? "#{sLocal}" : "#{sWorld}"); - mControllerButtons.x = global ? "#{sLocal}" : "#{sWorld}"; + mControllerButtons.mX = global ? "#{sLocal}" : "#{sWorld}"; MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } @@ -1475,8 +1475,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.a = "#{Interface:OK}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{Interface:OK}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } } @@ -1529,9 +1529,9 @@ namespace MWGui eventDeleteClicked(); } - ControllerButtonStr* EditNoteDialog::getControllerButtons() + ControllerButtons* EditNoteDialog::getControllerButtons() { - mControllerButtons.x = getDeleteButtonShown() ? "#{sDelete}" : ""; + mControllerButtons.mX = getDeleteButtonShown() ? "#{sDelete}" : ""; return &mControllerButtons; } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 8730964094..9a474e8170 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -212,7 +212,7 @@ namespace MWGui EventHandle_Void eventDeleteClicked; EventHandle_Void eventOkClicked; - ControllerButtonStr* getControllerButtons() override; + ControllerButtons* getControllerButtons() override; private: void onCancelButtonClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index adaab63a93..87cda0b4db 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -32,8 +32,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - mControllerButtons.a = "#{sRepair}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sRepair}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } } diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index d1f0727971..d705d66bb6 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -285,7 +285,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - mControllerButtons.a = "#{Interface:OK}"; + mControllerButtons.mA = "#{Interface:OK}"; // If we have more than one button, we need to set the focus to the first one. if (mButtons.size() > 1) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 8aacc6f148..782949b99a 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -62,8 +62,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:OK}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:OK}"; } } @@ -541,8 +541,8 @@ namespace MWGui { mDisableGamepadCursor = true; mItemButton->setStateSelected(true); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } center(); @@ -687,8 +687,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } center(); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index f1843fb4d7..a4125cfbdd 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -108,12 +108,12 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.lStick = "#{sMouse}"; - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; - mControllerButtons.y = "#{sSex}"; - mControllerButtons.l1 = "#{sHair}"; - mControllerButtons.r1 = "#{sFace}"; + mControllerButtons.mLStick = "#{sMouse}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{sBack}"; + mControllerButtons.mY = "#{sSex}"; + mControllerButtons.mL1 = "#{sHair}"; + mControllerButtons.mR1 = "#{sFace}"; } updateRaces(); @@ -130,13 +130,13 @@ namespace MWGui { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {}))); - mControllerButtons.x = "#{sNext}"; + mControllerButtons.mX = "#{sNext}"; } else if (Settings::gui().mControllerMenus) { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); - mControllerButtons.x = "#{sDone}"; + mControllerButtons.mX = "#{sDone}"; } else okButton->setCaption( diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 9c1e71c3fd..b6354c1d70 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -40,9 +40,9 @@ namespace MWGui mGemIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onSelectItem); - mControllerButtons.a = "#{OMWEngine:RechargeSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.y = "#{sSoulGem}"; + mControllerButtons.mA = "#{OMWEngine:RechargeSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mY = "#{sSoulGem}"; } void Recharge::onOpen() diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 13a3923d07..cb3f6be8bb 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -40,9 +40,9 @@ namespace MWGui mToolIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onSelectItem); - mControllerButtons.a = "#{sRepair}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.y = "#{OMWEngine:RepairTool}"; + mControllerButtons.mA = "#{sRepair}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mY = "#{OMWEngine:RepairTool}"; } void Repair::onOpen() diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 236c7198d2..66b48aa2ee 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -123,9 +123,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { setControllerFocus(mButtons, mControllerFocus, true); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; - mControllerButtons.x = "#{sDone}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{sBack}"; + mControllerButtons.mX = "#{sDone}"; okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index afe18455b5..28c1b17cda 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -67,8 +67,8 @@ namespace MWGui // To avoid accidental deletions mDeleteButton->setNeedKeyFocus(false); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } void SaveGameDialog::onSlotActivated(MyGUI::ListBox* sender, size_t pos) @@ -526,9 +526,9 @@ namespace MWGui mScreenshot->setRenderItemTexture(mScreenshotTexture.get()); } - ControllerButtonStr* SaveGameDialog::getControllerButtons() + ControllerButtons* SaveGameDialog::getControllerButtons() { - mControllerButtons.y = mSaving ? "" : "#{OMWEngine:LoadingSelectCharacter}"; + mControllerButtons.mY = mSaving ? "" : "#{OMWEngine:LoadingSelectCharacter}"; return &mControllerButtons; } diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 13c4588d3f..2b5b163044 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -24,7 +24,7 @@ namespace MWGui void setLoadOrSave(bool load); - ControllerButtonStr* getControllerButtons() override; + ControllerButtons* getControllerButtons() override; private: void confirmDeleteSave(); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 3986aa44bd..5b5f4a4ec4 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -40,8 +40,8 @@ namespace MWGui mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); mControllerScrollWidget = mTextView; - mControllerButtons.b = "#{Interface:Close}"; - mControllerButtons.dpad = "#{sScrolldown}"; + mControllerButtons.mB = "#{Interface:Close}"; + mControllerButtons.mDpad = "#{sScrolldown}"; center(); } @@ -128,9 +128,9 @@ namespace MWGui BookWindowBase::onClose(); } - ControllerButtonStr* ScrollWindow::getControllerButtons() + ControllerButtons* ScrollWindow::getControllerButtons() { - mControllerButtons.a = mTakeButton->getVisible() ? "#{sTake}" : ""; + mControllerButtons.mA = mTakeButton->getVisible() ? "#{sTake}" : ""; return &mControllerButtons; } diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index 314925c7a7..398843f824 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -25,7 +25,7 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Scroll"; } - ControllerButtonStr* getControllerButtons() override; + ControllerButtons* getControllerButtons() override; protected: void onCloseButtonClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index b1d69966ad..15e083d94e 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -463,9 +463,9 @@ namespace MWGui i++; } - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:OK}"; - mControllerButtons.lStick = "#{sMouse}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:OK}"; + mControllerButtons.mLStick = "#{sMouse}"; } void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 12b4e453a6..854b2f53ec 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -38,9 +38,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.r3 = "#{sInfo}"; + mControllerButtons.mA = "#{sBuy}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mR3 = "#{sInfo}"; } } diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 190793b77a..e30dd03fd5 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -100,9 +100,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.x = "#{Interface:OK}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mX = "#{Interface:OK}"; } } @@ -598,10 +598,10 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.x = "#{sBuy}"; - mControllerButtons.r3 = "#{sInfo}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mX = "#{sBuy}"; + mControllerButtons.mR3 = "#{sInfo}"; } } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index b5533c2f61..78c7fabbff 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -62,9 +62,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { setPinButtonVisible(false); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; - mControllerButtons.r3 = "#{sInfo}"; + mControllerButtons.mA = "#{sSelect}"; + mControllerButtons.mB = "#{sBack}"; + mControllerButtons.mR3 = "#{sInfo}"; } } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 1552a90295..9ae598052a 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -84,9 +84,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { setPinButtonVisible(false); - mControllerButtons.lStick = "#{sMouse}"; - mControllerButtons.rStick = "#{sScrolldown}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.mLStick = "#{sMouse}"; + mControllerButtons.mRStick = "#{sScrolldown}"; + mControllerButtons.mB = "#{sBack}"; } onWindowResize(t); diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 5594501c8c..d059ac9603 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -28,7 +28,7 @@ namespace MWGui // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); - mControllerButtons.a = "#{Interface:OK}"; + mControllerButtons.mA = "#{Interface:OK}"; } void TextInputDialog::setNextButtonShow(bool shown) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index ab0b58933a..0422162eae 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -188,11 +188,11 @@ namespace MWGui image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon( SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); - mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{Interface:Cancel}"; - mControllerButtons.x = "#{sOffer}"; - mControllerButtons.r3 = "#{sInfo}"; - mControllerButtons.l2 = "#{sInventory}"; + mControllerButtons.mA = "#{sBuy}"; + mControllerButtons.mB = "#{Interface:Cancel}"; + mControllerButtons.mX = "#{sOffer}"; + mControllerButtons.mR3 = "#{sInfo}"; + mControllerButtons.mL2 = "#{sInventory}"; } } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index ef11d8f16c..f5830b2cee 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -40,8 +40,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sBuy}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } } diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 185a7be3cf..9836887c2b 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -41,8 +41,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - mControllerButtons.a = "#{sTravel}"; - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mA = "#{sTravel}"; + mControllerButtons.mB = "#{Interface:Cancel}"; } } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 0943863a69..95219cb521 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -80,7 +80,7 @@ namespace MWGui mTimeAdvancer.eventInterrupted += MyGUI::newDelegate(this, &WaitDialog::onWaitingInterrupted); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &WaitDialog::onWaitingFinished); - mControllerButtons.b = "#{Interface:Cancel}"; + mControllerButtons.mB = "#{Interface:Cancel}"; mDisableGamepadCursor = Settings::gui().mControllerMenus; } @@ -328,10 +328,10 @@ namespace MWGui } } - ControllerButtonStr* WaitDialog::getControllerButtons() + ControllerButtons* WaitDialog::getControllerButtons() { - mControllerButtons.a = mSleeping ? "#{sRest}" : "#{sWait}"; - mControllerButtons.x = mSleeping && mUntilHealedButton->getVisible() ? "#{sUntilHealed}" : ""; + mControllerButtons.mA = mSleeping ? "#{sRest}" : "#{sWait}"; + mControllerButtons.mX = mSleeping && mUntilHealedButton->getVisible() ? "#{sUntilHealed}" : ""; return &mControllerButtons; } diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 19d7752946..fb25ddc1fa 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -46,7 +46,7 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "WaitDialog"; } - ControllerButtonStr* getControllerButtons() override; + ControllerButtons* getControllerButtons() override; protected: MyGUI::TextBox* mDateTimeText; diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index e4b4636246..c1f1b827a6 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -17,23 +17,23 @@ namespace MWGui int wrap(int index, int max); void setControllerFocus(const std::vector& buttons, int index, bool selected); - struct ControllerButtonStr + struct ControllerButtons { - std::string a; - std::string b; - std::string dpad; - std::string l1; - std::string l2; - std::string l3; - std::string lStick; - std::string menu; - std::string r1; - std::string r2; - std::string r3; - std::string rStick; - std::string view; - std::string x; - std::string y; + std::string mA; + std::string mB; + std::string mDpad; + std::string mL1; + std::string mL2; + std::string mL3; + std::string mLStick; + std::string mMenu; + std::string mR1; + std::string mR2; + std::string mR3; + std::string mRStick; + std::string mView; + std::string mX; + std::string mY; }; class WindowBase : public Layout @@ -78,7 +78,7 @@ namespace MWGui static void clampWindowCoordinates(MyGUI::Window* window); - virtual ControllerButtonStr* getControllerButtons() { return &mControllerButtons; } + virtual ControllerButtons* getControllerButtons() { return &mControllerButtons; } MyGUI::Widget* getControllerScrollWidget() { return mControllerScrollWidget; } bool isGamepadCursorAllowed() { return !mDisableGamepadCursor; } virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { return true; } @@ -88,7 +88,7 @@ namespace MWGui protected: virtual void onTitleDoubleClicked(); - ControllerButtonStr mControllerButtons; + ControllerButtons mControllerButtons; bool mActiveControllerWindow = false; bool mDisableGamepadCursor = false; MyGUI::Widget* mControllerScrollWidget = nullptr; From 7b9958247a797396fb18816472e080150be6a3d6 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 27 Jul 2025 14:08:13 -0700 Subject: [PATCH 062/102] Null ControllerButtons pointers are supported; they hide the button overlay --- .../openmw/mwgui/controllerbuttonsoverlay.cpp | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index fc4cdfaec2..725ea8a0a1 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -49,23 +49,26 @@ namespace MWGui void ControllerButtonsOverlay::setButtons(ControllerButtons* buttons) { int buttonCount = 0; - buttonCount += updateButton(Button::Button_A, buttons->mA); - buttonCount += updateButton(Button::Button_B, buttons->mB); - buttonCount += updateButton(Button::Button_Dpad, buttons->mDpad); - buttonCount += updateButton(Button::Button_L1, buttons->mL1); - buttonCount += updateButton(Button::Button_L2, buttons->mL2); - buttonCount += updateButton(Button::Button_L3, buttons->mL3); - buttonCount += updateButton(Button::Button_LStick, buttons->mLStick); - buttonCount += updateButton(Button::Button_Menu, buttons->mMenu); - buttonCount += updateButton(Button::Button_R1, buttons->mR1); - buttonCount += updateButton(Button::Button_R2, buttons->mR2); - buttonCount += updateButton(Button::Button_R3, buttons->mR3); - buttonCount += updateButton(Button::Button_RStick, buttons->mRStick); - buttonCount += updateButton(Button::Button_View, buttons->mView); - buttonCount += updateButton(Button::Button_X, buttons->mX); - buttonCount += updateButton(Button::Button_Y, buttons->mY); + if (buttons != nullptr) + { + buttonCount += updateButton(Button::Button_A, buttons->mA); + buttonCount += updateButton(Button::Button_B, buttons->mB); + buttonCount += updateButton(Button::Button_Dpad, buttons->mDpad); + buttonCount += updateButton(Button::Button_L1, buttons->mL1); + buttonCount += updateButton(Button::Button_L2, buttons->mL2); + buttonCount += updateButton(Button::Button_L3, buttons->mL3); + buttonCount += updateButton(Button::Button_LStick, buttons->mLStick); + buttonCount += updateButton(Button::Button_Menu, buttons->mMenu); + buttonCount += updateButton(Button::Button_R1, buttons->mR1); + buttonCount += updateButton(Button::Button_R2, buttons->mR2); + buttonCount += updateButton(Button::Button_R3, buttons->mR3); + buttonCount += updateButton(Button::Button_RStick, buttons->mRStick); + buttonCount += updateButton(Button::Button_View, buttons->mView); + buttonCount += updateButton(Button::Button_X, buttons->mX); + buttonCount += updateButton(Button::Button_Y, buttons->mY); - mHBox->notifyChildrenSizeChanged(); + mHBox->notifyChildrenSizeChanged(); + } setVisible(buttonCount > 0); } From 5d5d14a5a601cb186f0a3f1e33a79c9c927cfc76 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 29 Jul 2025 17:28:52 -0700 Subject: [PATCH 063/102] Fix spurious button presses causing controller tooltip to wiggle --- apps/openmw/mwgui/itemview.cpp | 76 ++++++++-------- apps/openmw/mwgui/spellbuyingwindow.cpp | 2 + apps/openmw/mwgui/spellcreationdialog.cpp | 2 + apps/openmw/mwgui/spellview.cpp | 105 ++++++++++++---------- 4 files changed, 100 insertions(+), 85 deletions(-) diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index a01eaad2e8..243837bb65 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -206,44 +206,46 @@ namespace MWGui int prevFocus = mControllerFocus; - if (button == SDL_CONTROLLER_BUTTON_A) + switch (button) { - // Select the focused item, if any. - if (mControllerFocus >= 0 && mControllerFocus < mItemCount) - { - MyGUI::Widget* dragArea = mScrollView->getChildAt(0); - onSelectedItem(dragArea->getChildAt(mControllerFocus)); - } - } - else if (button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) - { - // Toggle info tooltip - MWBase::Environment::get().getWindowManager()->setControllerTooltip( - !MWBase::Environment::get().getWindowManager()->getControllerTooltip()); - updateControllerFocus(-1, mControllerFocus); - } - else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP) - { - if (mControllerFocus % mRows == 0) - mControllerFocus = std::min(mControllerFocus + mRows - 1, mItemCount - 1); - else - mControllerFocus--; - } - else if (button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) - { - if (mControllerFocus % mRows == mRows - 1 || mControllerFocus == mItemCount - 1) - mControllerFocus -= mControllerFocus % mRows; - else - mControllerFocus++; - } - else if (button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mControllerFocus >= mRows) - mControllerFocus -= mRows; - else if (button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) - { - if (mControllerFocus + mRows < mItemCount) - mControllerFocus += mRows; - else if (mControllerFocus / mRows != (mItemCount - 1) / mRows) - mControllerFocus = mItemCount - 1; + case SDL_CONTROLLER_BUTTON_A: + // Select the focused item, if any. + if (mControllerFocus >= 0 && mControllerFocus < mItemCount) + { + MyGUI::Widget* dragArea = mScrollView->getChildAt(0); + onSelectedItem(dragArea->getChildAt(mControllerFocus)); + } + break; + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: + // Toggle info tooltip + MWBase::Environment::get().getWindowManager()->setControllerTooltip( + !MWBase::Environment::get().getWindowManager()->getControllerTooltip()); + updateControllerFocus(-1, mControllerFocus); + break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: + if (mControllerFocus % mRows == 0) + mControllerFocus = std::min(mControllerFocus + mRows - 1, mItemCount - 1); + else + mControllerFocus--; + break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + if (mControllerFocus % mRows == mRows - 1 || mControllerFocus == mItemCount - 1) + mControllerFocus -= mControllerFocus % mRows; + else + mControllerFocus++; + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + if (mControllerFocus >= mRows) + mControllerFocus -= mRows; + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + if (mControllerFocus + mRows < mItemCount) + mControllerFocus += mRows; + else if (mControllerFocus / mRows != (mItemCount - 1) / mRows) + mControllerFocus = mItemCount - 1; + break; + default: + return; } if (prevFocus != mControllerFocus) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 854b2f53ec..4a9f118d75 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -263,6 +263,8 @@ namespace MWGui mControllerFocus = wrap(mControllerFocus + 1, mSpellButtons.size()); mSpellButtons[mControllerFocus].first->setStateSelected(true); } + else + return true; if (mControllerFocus < mSpellButtons.size()) { diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index e30dd03fd5..558c25fb9a 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -1109,6 +1109,8 @@ namespace MWGui winMgr->setControllerTooltip(false); } + else + return true; // Scroll the list to keep the active item in view if (mAvailableFocus <= 5) diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index ee1755e706..ff77284bdf 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -351,58 +351,67 @@ namespace MWGui int prevFocus = mControllerFocus; - if (button == SDL_CONTROLLER_BUTTON_A) + switch (button) { - // Select the focused item, if any. - if (mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) - { - onSpellSelected(mButtons[mControllerFocus].first); - MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); - } - } - else if (button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) - { - // Toggle info tooltip - MWBase::Environment::get().getWindowManager()->setControllerTooltip( - !MWBase::Environment::get().getWindowManager()->getControllerTooltip()); - } - else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP) - mControllerFocus--; - else if (button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) - mControllerFocus++; - else if (button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) - mControllerFocus = std::max(0, mControllerFocus - 10); - else if (button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) - mControllerFocus = std::min(mControllerFocus + 10, static_cast(mButtons.size()) - 1); - else if (button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) - { - // Jump to first item in previous group - int prevGroupIndex = 0; - for (int groupIndex : mGroupIndices) - { - if (groupIndex >= mControllerFocus) - break; - else - prevGroupIndex = groupIndex; - } - mControllerFocus = prevGroupIndex; - } - else if (button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) - { - // Jump to first item in next group - int newFocus = mControllerFocus; - for (int groupIndex : mGroupIndices) - { - if (groupIndex > mControllerFocus) + case SDL_CONTROLLER_BUTTON_A: + // Select the focused item, if any. + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) { - newFocus = groupIndex; - break; + onSpellSelected(mButtons[mControllerFocus].first); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } + break; + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: + // Toggle info tooltip + MWBase::Environment::get().getWindowManager()->setControllerTooltip( + !MWBase::Environment::get().getWindowManager()->getControllerTooltip()); + break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: + mControllerFocus--; + break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + mControllerFocus++; + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + mControllerFocus = std::max(0, mControllerFocus - 10); + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + mControllerFocus = std::min(mControllerFocus + 10, static_cast(mButtons.size()) - 1); + break; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: + { + // Jump to first item in previous group + int prevGroupIndex = 0; + for (int groupIndex : mGroupIndices) + { + if (groupIndex >= mControllerFocus) + break; + else + prevGroupIndex = groupIndex; + } + mControllerFocus = prevGroupIndex; } - // If on last group, jump to bottom of whole list - if (newFocus == mControllerFocus) - newFocus = mButtons.size() - 1; - mControllerFocus = newFocus; + break; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: + { + // Jump to first item in next group + int newFocus = mControllerFocus; + for (int groupIndex : mGroupIndices) + { + if (groupIndex > mControllerFocus) + { + newFocus = groupIndex; + break; + } + } + // If on last group, jump to bottom of whole list + if (newFocus == mControllerFocus) + newFocus = mButtons.size() - 1; + mControllerFocus = newFocus; + } + break; + default: + return; } mControllerFocus = wrap(mControllerFocus, mButtons.size()); From 582b409f7f91cb79538eb411e4ac8fa97c5f1c6a Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 29 Jul 2025 19:29:20 -0700 Subject: [PATCH 064/102] Fix right stick from causing mouse to appear in controller mode --- apps/openmw/mwgui/windowbase.hpp | 2 +- apps/openmw/mwinput/controllermanager.cpp | 23 +++++++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index c1f1b827a6..5a84db4a5c 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -82,7 +82,7 @@ namespace MWGui MyGUI::Widget* getControllerScrollWidget() { return mControllerScrollWidget; } bool isGamepadCursorAllowed() { return !mDisableGamepadCursor; } virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { return true; } - virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { return true; } + virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { return false; } virtual void setActiveControllerWindow(bool active) { mActiveControllerWindow = active; } protected: diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 3b352decfc..c31fd0f3a5 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -221,7 +221,8 @@ namespace MWInput mJoystickLastUsed = true; if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { - gamepadToGuiControl(arg); + if (gamepadToGuiControl(arg)) + return; } else if (mBindingsManager->actionIsActive(A_TogglePOV) && (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT || arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT)) @@ -354,6 +355,7 @@ namespace MWInput MWGui::WindowBase* topWin = winMgr->getActiveControllerWindow(); if (topWin) { + bool isPastDeadzone = std::abs(arg.value) > 2000; // Update cursor state mGamepadGuiCursorEnabled = topWin->isGamepadCursorAllowed(); if (!mGamepadGuiCursorEnabled) @@ -363,23 +365,36 @@ namespace MWInput && (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY)) { // Treat the left stick like a cursor, which is the default behavior. - if (winMgr->getControllerTooltip() && std::abs(arg.value) > 2000) + if (isPastDeadzone && winMgr->getControllerTooltip()) { winMgr->setControllerTooltip(false); winMgr->setCursorVisible(true); } + else if (isPastDeadzone && mGamepadGuiCursorEnabled) + { + winMgr->setCursorVisible(true); + } return false; } - // On some windows, treat right stick like a scroll wheel. - if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTY && topWin->getControllerScrollWidget() != nullptr) + // Some windows have a specific widget to scroll with the right stick. Move the mouse there. + if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTY && isPastDeadzone + && topWin->getControllerScrollWidget() != nullptr) + { mMouseManager->warpMouseToWidget(topWin->getControllerScrollWidget()); + winMgr->setCursorVisible(false); + } if (topWin->onControllerThumbstickEvent(arg)) { // Window handled the event. return true; } + else if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTX || arg.axis == SDL_CONTROLLER_AXIS_RIGHTY) + { + // Only right-stick scroll if mouse is visible or there's a widget to scroll. + return !winMgr->getCursorVisible() && topWin->getControllerScrollWidget() == nullptr; + } } } From 23e3d0b49aac7978e8f5bb762bb0f171ba005b66 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 31 Jul 2025 17:50:14 -0700 Subject: [PATCH 065/102] Initialize inventory tabs in a loop --- apps/openmw/mwgui/inventorytabsoverlay.cpp | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/inventorytabsoverlay.cpp b/apps/openmw/mwgui/inventorytabsoverlay.cpp index 7464a3b1ec..940e9fec7d 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.cpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.cpp @@ -14,22 +14,14 @@ namespace MWGui : WindowBase("openmw_inventory_tabs.layout") { MyGUI::Button* tab; + constexpr char* kTabIds[] = { "TabMap", "TabInventory", "TabSpells", "TabStats" }; - getWidget(tab, "TabMap"); - tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); - mTabs.push_back(tab); - - getWidget(tab, "TabInventory"); - tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); - mTabs.push_back(tab); - - getWidget(tab, "TabSpells"); - tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); - mTabs.push_back(tab); - - getWidget(tab, "TabStats"); - tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); - mTabs.push_back(tab); + for (const char* id : kTabIds) + { + getWidget(tab, id); + tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); + mTabs.push_back(tab); + } MyGUI::ImageBox* image; getWidget(image, "BtnL2Image"); From 5de1ae7b24018b24b0334bde8da1673875ae20ea Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 31 Jul 2025 17:52:35 -0700 Subject: [PATCH 066/102] Simplify controller button overlay even more --- .../openmw/mwgui/controllerbuttonsoverlay.cpp | 73 ++++++++++--------- .../openmw/mwgui/controllerbuttonsoverlay.hpp | 16 ++-- 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index 725ea8a0a1..e4181e4bd5 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -8,33 +8,49 @@ namespace MWGui { + static constexpr ControllerButtonsOverlay::ButtonDefinition sButtonDefs[] = { + { ControllerButtonsOverlay::Button::Button_A, "A", SDL_CONTROLLER_BUTTON_A, &ControllerButtons::mA }, + { ControllerButtonsOverlay::Button::Button_B, "B", SDL_CONTROLLER_BUTTON_B, &ControllerButtons::mB }, + { ControllerButtonsOverlay::Button::Button_Dpad, "Dpad", SDL_CONTROLLER_BUTTON_DPAD_UP, + &ControllerButtons::mDpad }, + { ControllerButtonsOverlay::Button::Button_L1, "L1", SDL_CONTROLLER_BUTTON_LEFTSHOULDER, + &ControllerButtons::mL1 }, + { ControllerButtonsOverlay::Button::Button_L2, "L2", SDL_CONTROLLER_AXIS_TRIGGERLEFT, &ControllerButtons::mL2 }, + { ControllerButtonsOverlay::Button::Button_L3, "L3", SDL_CONTROLLER_BUTTON_LEFTSTICK, &ControllerButtons::mL3 }, + { ControllerButtonsOverlay::Button::Button_LStick, "LStick", SDL_CONTROLLER_AXIS_LEFTY, + &ControllerButtons::mLStick }, + { ControllerButtonsOverlay::Button::Button_Menu, "Menu", SDL_CONTROLLER_BUTTON_BACK, + &ControllerButtons::mMenu }, + { ControllerButtonsOverlay::Button::Button_R1, "R1", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, + &ControllerButtons::mR1 }, + { ControllerButtonsOverlay::Button::Button_R2, "R2", SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + &ControllerButtons::mR2 }, + { ControllerButtonsOverlay::Button::Button_R3, "R3", SDL_CONTROLLER_BUTTON_RIGHTSTICK, + &ControllerButtons::mR3 }, + { ControllerButtonsOverlay::Button::Button_RStick, "RStick", SDL_CONTROLLER_AXIS_RIGHTY, + &ControllerButtons::mRStick }, + { ControllerButtonsOverlay::Button::Button_View, "View", SDL_CONTROLLER_BUTTON_START, + &ControllerButtons::mView }, + { ControllerButtonsOverlay::Button::Button_X, "X", SDL_CONTROLLER_BUTTON_X, &ControllerButtons::mX }, + { ControllerButtonsOverlay::Button::Button_Y, "Y", SDL_CONTROLLER_BUTTON_Y, &ControllerButtons::mY }, + }; + ControllerButtonsOverlay::ControllerButtonsOverlay() : WindowBase("openmw_controllerbuttons.layout") { MWBase::InputManager* inputMgr = MWBase::Environment::get().getInputManager(); - mButtons[Button::Button_A] = { "A", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_A) }; - mButtons[Button::Button_B] = { "B", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_B) }; - mButtons[Button::Button_Dpad] = { "Dpad", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_DPAD_UP) }; - mButtons[Button::Button_L1] = { "L1", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_LEFTSHOULDER) }; - mButtons[Button::Button_L2] = { "L2", inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERLEFT) }; - mButtons[Button::Button_L3] = { "L3", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_LEFTSTICK) }; - mButtons[Button::Button_LStick] = { "LStick", inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_LEFTY) }; - mButtons[Button::Button_Menu] = { "Menu", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_BACK) }; - mButtons[Button::Button_R1] = { "R1", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) }; - mButtons[Button::Button_R2] = { "R2", inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERRIGHT) }; - mButtons[Button::Button_R3] = { "R3", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_RIGHTSTICK) }; - mButtons[Button::Button_RStick] = { "RStick", inputMgr->getControllerAxisIcon(SDL_CONTROLLER_AXIS_RIGHTY) }; - mButtons[Button::Button_View] = { "View", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_START) }; - mButtons[Button::Button_X] = { "X", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_X) }; - mButtons[Button::Button_Y] = { "Y", inputMgr->getControllerButtonIcon(SDL_CONTROLLER_BUTTON_Y) }; - for (size_t i = 0; i < mButtons.size(); i++) { - getWidget(mButtons[i].mImage, "Btn" + mButtons[i].mLayoutName + "Image"); - getWidget(mButtons[i].mText, "Btn" + mButtons[i].mLayoutName + "Text"); - getWidget(mButtons[i].mHBox, "Btn" + mButtons[i].mLayoutName); - setIcon(mButtons[i].mImage, mButtons[i].mImagePath); + getWidget(mButtons[i].mImage, "Btn" + sButtonDefs[i].mName + "Image"); + getWidget(mButtons[i].mText, "Btn" + sButtonDefs[i].mName + "Text"); + getWidget(mButtons[i].mHBox, "Btn" + sButtonDefs[i].mName); + if (std::holds_alternative(sButtonDefs[i].mId)) + setIcon(mButtons[i].mImage, + inputMgr->getControllerAxisIcon(std::get(sButtonDefs[i].mId))); + else + setIcon(mButtons[i].mImage, + inputMgr->getControllerButtonIcon(std::get(sButtonDefs[i].mId))); } getWidget(mHBox, "ButtonBox"); @@ -51,21 +67,8 @@ namespace MWGui int buttonCount = 0; if (buttons != nullptr) { - buttonCount += updateButton(Button::Button_A, buttons->mA); - buttonCount += updateButton(Button::Button_B, buttons->mB); - buttonCount += updateButton(Button::Button_Dpad, buttons->mDpad); - buttonCount += updateButton(Button::Button_L1, buttons->mL1); - buttonCount += updateButton(Button::Button_L2, buttons->mL2); - buttonCount += updateButton(Button::Button_L3, buttons->mL3); - buttonCount += updateButton(Button::Button_LStick, buttons->mLStick); - buttonCount += updateButton(Button::Button_Menu, buttons->mMenu); - buttonCount += updateButton(Button::Button_R1, buttons->mR1); - buttonCount += updateButton(Button::Button_R2, buttons->mR2); - buttonCount += updateButton(Button::Button_R3, buttons->mR3); - buttonCount += updateButton(Button::Button_RStick, buttons->mRStick); - buttonCount += updateButton(Button::Button_View, buttons->mView); - buttonCount += updateButton(Button::Button_X, buttons->mX); - buttonCount += updateButton(Button::Button_Y, buttons->mY); + for (const auto& row : sButtonDefs) + buttonCount += updateButton(row.mButton, buttons->*(row.mField)); mHBox->notifyChildrenSizeChanged(); } diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index 0c7d484e6c..ec462189ef 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -18,7 +18,6 @@ namespace MWGui int getHeight(); void setButtons(ControllerButtons* buttons); - private: enum Button { Button_A = 0, @@ -39,16 +38,23 @@ namespace MWGui Button_Max, }; - struct ButtonDetails + struct ButtonDefinition + { + Button mButton; + std::string mName; + std::variant mId; + std::string MWGui::ControllerButtons::* mField; + }; + + private: + struct ButtonWidgets { - std::string mLayoutName; - std::string mImagePath; MyGUI::ImageBox* mImage = nullptr; MyGUI::TextBox* mText = nullptr; Gui::HBox* mHBox = nullptr; }; - std::array mButtons; + std::array mButtons; Gui::HBox* mHBox; From a134f8d88250e287b075b75d6f2ee0d73c1ec16b Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 31 Jul 2025 18:01:08 -0700 Subject: [PATCH 067/102] Fix clang warning --- apps/openmw/mwgui/controllerbuttonsoverlay.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index ec462189ef..4cbf32cb47 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -43,7 +43,7 @@ namespace MWGui Button mButton; std::string mName; std::variant mId; - std::string MWGui::ControllerButtons::* mField; + std::string MWGui::ControllerButtons::*mField; }; private: From 666f154082023e360411a2206fb034126dfa0bad Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 31 Jul 2025 22:13:56 -0700 Subject: [PATCH 068/102] Fix clang warnings in Ubuntu --- apps/openmw/mwgui/inventorytabsoverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/inventorytabsoverlay.cpp b/apps/openmw/mwgui/inventorytabsoverlay.cpp index 940e9fec7d..d95079ab53 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.cpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.cpp @@ -14,7 +14,7 @@ namespace MWGui : WindowBase("openmw_inventory_tabs.layout") { MyGUI::Button* tab; - constexpr char* kTabIds[] = { "TabMap", "TabInventory", "TabSpells", "TabStats" }; + static const char* kTabIds[] = { "TabMap", "TabInventory", "TabSpells", "TabStats" }; for (const char* id : kTabIds) { From f453ce27a0abc743c629af9c94aa6c3e8ba27bce Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 31 Jul 2025 22:22:21 -0700 Subject: [PATCH 069/102] Remove uses of std::variant and std::holds_alternative because they're not supported on Ubuntu --- .../openmw/mwgui/controllerbuttonsoverlay.cpp | 63 ++++++++++--------- .../openmw/mwgui/controllerbuttonsoverlay.hpp | 21 +++++-- 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index e4181e4bd5..1d937c1e63 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -9,30 +9,36 @@ namespace MWGui { static constexpr ControllerButtonsOverlay::ButtonDefinition sButtonDefs[] = { - { ControllerButtonsOverlay::Button::Button_A, "A", SDL_CONTROLLER_BUTTON_A, &ControllerButtons::mA }, - { ControllerButtonsOverlay::Button::Button_B, "B", SDL_CONTROLLER_BUTTON_B, &ControllerButtons::mB }, - { ControllerButtonsOverlay::Button::Button_Dpad, "Dpad", SDL_CONTROLLER_BUTTON_DPAD_UP, - &ControllerButtons::mDpad }, - { ControllerButtonsOverlay::Button::Button_L1, "L1", SDL_CONTROLLER_BUTTON_LEFTSHOULDER, - &ControllerButtons::mL1 }, - { ControllerButtonsOverlay::Button::Button_L2, "L2", SDL_CONTROLLER_AXIS_TRIGGERLEFT, &ControllerButtons::mL2 }, - { ControllerButtonsOverlay::Button::Button_L3, "L3", SDL_CONTROLLER_BUTTON_LEFTSTICK, &ControllerButtons::mL3 }, - { ControllerButtonsOverlay::Button::Button_LStick, "LStick", SDL_CONTROLLER_AXIS_LEFTY, - &ControllerButtons::mLStick }, - { ControllerButtonsOverlay::Button::Button_Menu, "Menu", SDL_CONTROLLER_BUTTON_BACK, - &ControllerButtons::mMenu }, - { ControllerButtonsOverlay::Button::Button_R1, "R1", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, - &ControllerButtons::mR1 }, - { ControllerButtonsOverlay::Button::Button_R2, "R2", SDL_CONTROLLER_AXIS_TRIGGERRIGHT, - &ControllerButtons::mR2 }, - { ControllerButtonsOverlay::Button::Button_R3, "R3", SDL_CONTROLLER_BUTTON_RIGHTSTICK, - &ControllerButtons::mR3 }, - { ControllerButtonsOverlay::Button::Button_RStick, "RStick", SDL_CONTROLLER_AXIS_RIGHTY, - &ControllerButtons::mRStick }, - { ControllerButtonsOverlay::Button::Button_View, "View", SDL_CONTROLLER_BUTTON_START, - &ControllerButtons::mView }, - { ControllerButtonsOverlay::Button::Button_X, "X", SDL_CONTROLLER_BUTTON_X, &ControllerButtons::mX }, - { ControllerButtonsOverlay::Button::Button_Y, "Y", SDL_CONTROLLER_BUTTON_Y, &ControllerButtons::mY }, + { ControllerButtonsOverlay::Button::Button_A, "A", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_A }, &ControllerButtons::mA }, + { ControllerButtonsOverlay::Button::Button_B, "B", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_B }, &ControllerButtons::mB }, + { ControllerButtonsOverlay::Button::Button_Dpad, "Dpad", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_DPAD_UP }, &ControllerButtons::mDpad }, + { ControllerButtonsOverlay::Button::Button_L1, "L1", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_LEFTSHOULDER }, &ControllerButtons::mL1 }, + { ControllerButtonsOverlay::Button::Button_L2, "L2", ControllerButtonsOverlay::InputType_Axis, + { .mAxis = SDL_CONTROLLER_AXIS_TRIGGERLEFT }, &ControllerButtons::mL2 }, + { ControllerButtonsOverlay::Button::Button_L3, "L3", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_LEFTSTICK }, &ControllerButtons::mL3 }, + { ControllerButtonsOverlay::Button::Button_LStick, "LStick", ControllerButtonsOverlay::InputType_Axis, + { .mAxis = SDL_CONTROLLER_AXIS_LEFTY }, &ControllerButtons::mLStick }, + { ControllerButtonsOverlay::Button::Button_Menu, "Menu", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_BACK }, &ControllerButtons::mMenu }, + { ControllerButtonsOverlay::Button::Button_R1, "R1", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER }, &ControllerButtons::mR1 }, + { ControllerButtonsOverlay::Button::Button_R2, "R2", ControllerButtonsOverlay::InputType_Axis, + { .mAxis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT }, &ControllerButtons::mR2 }, + { ControllerButtonsOverlay::Button::Button_R3, "R3", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_RIGHTSTICK }, &ControllerButtons::mR3 }, + { ControllerButtonsOverlay::Button::Button_RStick, "RStick", ControllerButtonsOverlay::InputType_Axis, + { .mAxis = SDL_CONTROLLER_AXIS_RIGHTY }, &ControllerButtons::mRStick }, + { ControllerButtonsOverlay::Button::Button_View, "View", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_START }, &ControllerButtons::mView }, + { ControllerButtonsOverlay::Button::Button_X, "X", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_X }, &ControllerButtons::mX }, + { ControllerButtonsOverlay::Button::Button_Y, "Y", ControllerButtonsOverlay::InputType_Button, + { .mButton = SDL_CONTROLLER_BUTTON_Y }, &ControllerButtons::mY }, }; ControllerButtonsOverlay::ControllerButtonsOverlay() @@ -45,12 +51,11 @@ namespace MWGui getWidget(mButtons[i].mImage, "Btn" + sButtonDefs[i].mName + "Image"); getWidget(mButtons[i].mText, "Btn" + sButtonDefs[i].mName + "Text"); getWidget(mButtons[i].mHBox, "Btn" + sButtonDefs[i].mName); - if (std::holds_alternative(sButtonDefs[i].mId)) - setIcon(mButtons[i].mImage, - inputMgr->getControllerAxisIcon(std::get(sButtonDefs[i].mId))); + + if (sButtonDefs[i].mInputType == InputType_Axis) + setIcon(mButtons[i].mImage, inputMgr->getControllerAxisIcon(sButtonDefs[i].mId.mAxis)); else - setIcon(mButtons[i].mImage, - inputMgr->getControllerButtonIcon(std::get(sButtonDefs[i].mId))); + setIcon(mButtons[i].mImage, inputMgr->getControllerButtonIcon(sButtonDefs[i].mId.mButton)); } getWidget(mHBox, "ButtonBox"); diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index 4cbf32cb47..e828556e94 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -38,24 +38,35 @@ namespace MWGui Button_Max, }; + enum InputType + { + InputType_Button, + InputType_Axis + }; + struct ButtonDefinition { Button mButton; std::string mName; - std::variant mId; + InputType mInputType; + union { + SDL_GameControllerButton mButton; + SDL_GameControllerAxis mAxis; + } mId; std::string MWGui::ControllerButtons::*mField; }; private: struct ButtonWidgets { - MyGUI::ImageBox* mImage = nullptr; - MyGUI::TextBox* mText = nullptr; - Gui::HBox* mHBox = nullptr; + MyGUI::ImageBox* mImage; + MyGUI::TextBox* mText; + Gui::HBox* mHBox; + + ButtonWidgets() : mImage(nullptr), mText(nullptr), mHBox(nullptr) {} }; std::array mButtons; - Gui::HBox* mHBox; void setIcon(MyGUI::ImageBox* image, const std::string& imagePath); From d49a6a119bea801e1ded437ddd7d594180c05bc6 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 31 Jul 2025 22:27:31 -0700 Subject: [PATCH 070/102] Fix clang warnings --- apps/openmw/mwgui/controllerbuttonsoverlay.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index e828556e94..cf38505bc2 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -49,7 +49,8 @@ namespace MWGui Button mButton; std::string mName; InputType mInputType; - union { + union + { SDL_GameControllerButton mButton; SDL_GameControllerAxis mAxis; } mId; @@ -63,7 +64,12 @@ namespace MWGui MyGUI::TextBox* mText; Gui::HBox* mHBox; - ButtonWidgets() : mImage(nullptr), mText(nullptr), mHBox(nullptr) {} + ButtonWidgets() + : mImage(nullptr) + , mText(nullptr) + , mHBox(nullptr) + { + } }; std::array mButtons; From 2e5836a748b7359b25f2a5ec4e43219c4f10eac3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 31 Jul 2025 23:58:00 -0700 Subject: [PATCH 071/102] Remove hardcoded map size --- apps/openmw/mwgui/mapwindow.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 06bf8ed50d..c883718214 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1447,8 +1447,11 @@ namespace MWGui // Fill the screen, or limit to a certain size on large screens. Size chosen to // show the entire local map without scrolling. MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - int width = std::min(viewSize.width, 1552); - int height = std::min(winMgr->getControllerMenuHeight(), 1572); + MyGUI::IntSize canvasSize = mLocalMap->getCanvasSize(); + MyGUI::IntSize borderSize = mMainWidget->getSize() - mMainWidget->getClientWidget()->getSize(); + + int width = std::min(viewSize.width, canvasSize.width + borderSize.width); + int height = std::min(winMgr->getControllerMenuHeight(), canvasSize.height + borderSize.height); int x = (viewSize.width - width) / 2; int y = (viewSize.height - height) / 2; From 2cd4b643d052a7531113d242989bd7e5014368e6 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 15:18:55 -0700 Subject: [PATCH 072/102] npc stuff --- apps/openmw/mwlua/objectbindings.cpp | 2 +- apps/openmw/mwlua/types/npc.cpp | 134 ++++++++++++++++++++++++++- apps/openmw/mwlua/worldbindings.cpp | 10 ++ 3 files changed, 144 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 3508fdcd44..708792fa51 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -388,7 +388,7 @@ namespace MWLua objectT["setScale"] = [context](const GObject& object, float scale) { context.mLuaManager->addAction( [object, scale] { MWBase::Environment::get().getWorld()->scaleObject(object.ptr(), scale); }); - }; + };--adjustScale objectT["addScript"] = [context](const GObject& object, std::string_view path, sol::object initData) { const LuaUtil::ScriptsConfiguration& cfg = context.mLua->getConfiguration(); std::optional scriptId = cfg.findId(VFS::Path::Normalized(path)); diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 380a2d1e9b..60f6325ea2 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -29,6 +29,138 @@ namespace sol }; } +namespace +{ +ESM::NPC tableToNPC(const sol::table& rec) +{ + ESM::NPC npc; + + // Start from template if provided + if (rec["template"] != sol::nil) + npc = LuaUtil::cast(rec["template"]); + else + npc.blank(); + + // Force dummy ID + npc.mId = ESM::RefId::deserializeText("blank"); + + // Basic fields + if (rec["name"] != sol::nil) + npc.mName = rec["name"]; + if (rec["model"] != sol::nil) + npc.mModel = Misc::ResourceHelpers::meshPathForESM3(rec["model"].get()); + if (rec["mwscript"] != sol::nil) + npc.mScript = ESM::RefId::deserializeText(rec["mwscript"].get()); + if (rec["race"] != sol::nil) + npc.mRace = ESM::RefId::deserializeText(rec["race"].get()); + if (rec["class"] != sol::nil) + npc.mClass = ESM::RefId::deserializeText(rec["class"].get()); + if (rec["head"] != sol::nil) + npc.mHead = ESM::RefId::deserializeText(rec["head"].get()); + if (rec["hair"] != sol::nil) + npc.mHair = ESM::RefId::deserializeText(rec["hair"].get()); + + if (rec["isMale"] != sol::nil) + { + bool male = rec["isMale"]; + if (male) + npc.mFlags &= ~ESM::NPC::Female; + else + npc.mFlags |= ESM::NPC::Female; + } + + if (rec["isEssential"] != sol::nil) + { + bool essential = rec["isEssential"]; + if (essential) + npc.mFlags |= ESM::NPC::Essential; + else + npc.mFlags &= ~ESM::NPC::Essential; + } + + if (rec["isRespawning"] != sol::nil) + { + bool respawn = rec["isRespawning"]; + if (respawn) + npc.mFlags |= ESM::NPC::Respawn; + else + npc.mFlags &= ~ESM::NPC::Respawn; + } + + if (rec["baseDisposition"] != sol::nil) + npc.mNpdt.mDisposition = static_cast(rec["baseDisposition"]); + + if (rec["baseGold"] != sol::nil) + npc.mNpdt.mGold = static_cast(rec["baseGold"]); + + if (rec["bloodType"] != sol::nil) + npc.mBloodType = static_cast(rec["bloodType"]); + + // Services offered + if (rec["servicesOffered"] != sol::nil) + { + const sol::table services = rec["servicesOffered"]; + int flags = 0; + auto setFlag = [&](const char* key, int mask) { + if (services[key] != sol::nil && services[key]) + flags |= mask; + }; + + setFlag("Spells", ESM::NPC::Spells); + setFlag("Spellmaking", ESM::NPC::Spellmaking); + setFlag("Enchanting", ESM::NPC::Enchanting); + setFlag("Training", ESM::NPC::Training); + setFlag("Repair", ESM::NPC::Repair); + setFlag("Barter", ESM::NPC::AllItems); + setFlag("Weapon", ESM::NPC::Weapon); + setFlag("Armor", ESM::NPC::Armor); + setFlag("Clothing", ESM::NPC::Clothing); + setFlag("Books", ESM::NPC::Books); + setFlag("Ingredients", ESM::NPC::Ingredients); + setFlag("Picks", ESM::NPC::Picks); + setFlag("Probes", ESM::NPC::Probes); + setFlag("Lights", ESM::NPC::Lights); + setFlag("Apparatus", ESM::NPC::Apparatus); + setFlag("RepairItem", ESM::NPC::RepairItem); + setFlag("Misc", ESM::NPC::Misc); + setFlag("Potions", ESM::NPC::Potions); + setFlag("MagicItems", ESM::NPC::MagicItems); + + npc.mAiData.mServices = flags; + } + + // Travel destinations + if (rec["travelDestinations"] != sol::nil) + { + const sol::table travelDests = rec["travelDestinations"]; + npc.mTransport.clear(); + for (std::size_t i = 1; i <= travelDests.size(); ++i) + { + sol::table t = travelDests[i]; + ESM::Transport destination; + + // Position + destination.mPos.pos = t["position"]; + destination.mPos.rot = Misc::Convert::toRotation(LuaUtil::fromTransform(t["rotation"])); + + // Cell + std::string cellId = t["cellId"]; + destination.mCellName = cellId; // If empty, it will be handled as exterior + + npc.mTransport.push_back(destination); + } + } + + return npc; +} + // Blood type + if (rec["bloodType"] != sol::nil) + npc.mBloodType = static_cast(rec["bloodType"]); + + return npc; + } +} + namespace { size_t getValidRanksCount(const ESM::Faction* faction) @@ -151,7 +283,7 @@ namespace MWLua auto& stats = cls.getNpcStats(o.ptr()); stats.setBaseDisposition(stats.getBaseDisposition() + value); }; - + npc["createRecordDraft"] = tableToNPC; npc["getFactionRank"] = [](const Object& actor, std::string_view faction) -> size_t { const MWWorld::Ptr ptr = actor.ptr(); ESM::RefId factionId = parseFactionId(faction); diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index c02bad3bd3..de8eec3710 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -155,6 +156,11 @@ namespace MWLua MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, cell, count.value_or(1)); return GObject(newPtr); }; + api["advanceTime"] = [context](double hours, bool incremental) { + context.mLuaManager->addAction([ hours, incremental] { + MWBase::Environment::get().getWorld()->advanceTime(hours, incremental); + }); + }; api["getObjectByFormId"] = [](std::string_view formIdStr) -> GObject { ESM::RefId refId = ESM::RefId::deserializeText(formIdStr); if (!refId.is()) @@ -188,6 +194,10 @@ namespace MWLua checkGameInitialized(lua); return MWBase::Environment::get().getESMStore()->insert(potion); }, + [lua = context.mLua](const ESM::NPC& npc) -> const ESM::NPC* { + checkGameInitialized(lua); + return MWBase::Environment::get().getESMStore()->insert(npc); + }, [lua = context.mLua](const ESM::Weapon& weapon) -> const ESM::Weapon* { checkGameInitialized(lua); return MWBase::Environment::get().getESMStore()->insert(weapon); From abb381a163e177331c7758fe56bd3ff370de1cd1 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 16:10:49 -0700 Subject: [PATCH 073/102] Fix issues --- apps/openmw/mwlua/objectbindings.cpp | 2 +- apps/openmw/mwlua/types/npc.cpp | 202 ++++++++++++--------------- 2 files changed, 88 insertions(+), 116 deletions(-) diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 708792fa51..3508fdcd44 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -388,7 +388,7 @@ namespace MWLua objectT["setScale"] = [context](const GObject& object, float scale) { context.mLuaManager->addAction( [object, scale] { MWBase::Environment::get().getWorld()->scaleObject(object.ptr(), scale); }); - };--adjustScale + }; objectT["addScript"] = [context](const GObject& object, std::string_view path, sol::object initData) { const LuaUtil::ScriptsConfiguration& cfg = context.mLua->getConfiguration(); std::optional scriptId = cfg.findId(VFS::Path::Normalized(path)); diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 60f6325ea2..705e0831fd 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -31,132 +31,104 @@ namespace sol namespace { -ESM::NPC tableToNPC(const sol::table& rec) -{ - ESM::NPC npc; - - // Start from template if provided - if (rec["template"] != sol::nil) - npc = LuaUtil::cast(rec["template"]); - else - npc.blank(); - - // Force dummy ID - npc.mId = ESM::RefId::deserializeText("blank"); - - // Basic fields - if (rec["name"] != sol::nil) - npc.mName = rec["name"]; - if (rec["model"] != sol::nil) - npc.mModel = Misc::ResourceHelpers::meshPathForESM3(rec["model"].get()); - if (rec["mwscript"] != sol::nil) - npc.mScript = ESM::RefId::deserializeText(rec["mwscript"].get()); - if (rec["race"] != sol::nil) - npc.mRace = ESM::RefId::deserializeText(rec["race"].get()); - if (rec["class"] != sol::nil) - npc.mClass = ESM::RefId::deserializeText(rec["class"].get()); - if (rec["head"] != sol::nil) - npc.mHead = ESM::RefId::deserializeText(rec["head"].get()); - if (rec["hair"] != sol::nil) - npc.mHair = ESM::RefId::deserializeText(rec["hair"].get()); - - if (rec["isMale"] != sol::nil) + ESM::NPC tableToNPC(const sol::table& rec) { - bool male = rec["isMale"]; - if (male) - npc.mFlags &= ~ESM::NPC::Female; + ESM::NPC npc; + + // Start from template if provided + if (rec["template"] != sol::nil) + npc = LuaUtil::cast(rec["template"]); else - npc.mFlags |= ESM::NPC::Female; - } + npc.blank(); - if (rec["isEssential"] != sol::nil) - { - bool essential = rec["isEssential"]; - if (essential) - npc.mFlags |= ESM::NPC::Essential; - else - npc.mFlags &= ~ESM::NPC::Essential; - } + // Force dummy ID + npc.mId = ESM::RefId::deserializeText("blank"); - if (rec["isRespawning"] != sol::nil) - { - bool respawn = rec["isRespawning"]; - if (respawn) - npc.mFlags |= ESM::NPC::Respawn; - else - npc.mFlags &= ~ESM::NPC::Respawn; - } + // Basic fields + if (rec["name"] != sol::nil) + npc.mName = rec["name"]; + if (rec["model"] != sol::nil) + npc.mModel = Misc::ResourceHelpers::meshPathForESM3(rec["model"].get()); + if (rec["mwscript"] != sol::nil) + npc.mScript = ESM::RefId::deserializeText(rec["mwscript"].get()); + if (rec["race"] != sol::nil) + npc.mRace = ESM::RefId::deserializeText(rec["race"].get()); + if (rec["class"] != sol::nil) + npc.mClass = ESM::RefId::deserializeText(rec["class"].get()); + if (rec["head"] != sol::nil) + npc.mHead = ESM::RefId::deserializeText(rec["head"].get()); + if (rec["hair"] != sol::nil) + npc.mHair = ESM::RefId::deserializeText(rec["hair"].get()); - if (rec["baseDisposition"] != sol::nil) - npc.mNpdt.mDisposition = static_cast(rec["baseDisposition"]); - - if (rec["baseGold"] != sol::nil) - npc.mNpdt.mGold = static_cast(rec["baseGold"]); - - if (rec["bloodType"] != sol::nil) - npc.mBloodType = static_cast(rec["bloodType"]); - - // Services offered - if (rec["servicesOffered"] != sol::nil) - { - const sol::table services = rec["servicesOffered"]; - int flags = 0; - auto setFlag = [&](const char* key, int mask) { - if (services[key] != sol::nil && services[key]) - flags |= mask; - }; - - setFlag("Spells", ESM::NPC::Spells); - setFlag("Spellmaking", ESM::NPC::Spellmaking); - setFlag("Enchanting", ESM::NPC::Enchanting); - setFlag("Training", ESM::NPC::Training); - setFlag("Repair", ESM::NPC::Repair); - setFlag("Barter", ESM::NPC::AllItems); - setFlag("Weapon", ESM::NPC::Weapon); - setFlag("Armor", ESM::NPC::Armor); - setFlag("Clothing", ESM::NPC::Clothing); - setFlag("Books", ESM::NPC::Books); - setFlag("Ingredients", ESM::NPC::Ingredients); - setFlag("Picks", ESM::NPC::Picks); - setFlag("Probes", ESM::NPC::Probes); - setFlag("Lights", ESM::NPC::Lights); - setFlag("Apparatus", ESM::NPC::Apparatus); - setFlag("RepairItem", ESM::NPC::RepairItem); - setFlag("Misc", ESM::NPC::Misc); - setFlag("Potions", ESM::NPC::Potions); - setFlag("MagicItems", ESM::NPC::MagicItems); - - npc.mAiData.mServices = flags; - } - - // Travel destinations - if (rec["travelDestinations"] != sol::nil) - { - const sol::table travelDests = rec["travelDestinations"]; - npc.mTransport.clear(); - for (std::size_t i = 1; i <= travelDests.size(); ++i) + if (rec["isMale"] != sol::nil) { - sol::table t = travelDests[i]; - ESM::Transport destination; - - // Position - destination.mPos.pos = t["position"]; - destination.mPos.rot = Misc::Convert::toRotation(LuaUtil::fromTransform(t["rotation"])); - - // Cell - std::string cellId = t["cellId"]; - destination.mCellName = cellId; // If empty, it will be handled as exterior - - npc.mTransport.push_back(destination); + bool male = rec["isMale"]; + if (male) + npc.mFlags &= ~ESM::NPC::Female; + else + npc.mFlags |= ESM::NPC::Female; } - } - return npc; -} - // Blood type + if (rec["isEssential"] != sol::nil) + { + bool essential = rec["isEssential"]; + if (essential) + npc.mFlags |= ESM::NPC::Essential; + else + npc.mFlags &= ~ESM::NPC::Essential; + } + + if (rec["isRespawning"] != sol::nil) + { + bool respawn = rec["isRespawning"]; + if (respawn) + npc.mFlags |= ESM::NPC::Respawn; + else + npc.mFlags &= ~ESM::NPC::Respawn; + } + + if (rec["baseDisposition"] != sol::nil) + npc.mNpdt.mDisposition = static_cast(rec["baseDisposition"]); + + if (rec["baseGold"] != sol::nil) + npc.mNpdt.mGold = static_cast(rec["baseGold"]); + if (rec["bloodType"] != sol::nil) npc.mBloodType = static_cast(rec["bloodType"]); + // Services offered + if (rec["servicesOffered"] != sol::nil) + { + const sol::table services = rec["servicesOffered"]; + int flags = 0; + auto setFlag = [&](const char* key, int mask) { + if (services[key] != sol::nil && services[key]) + flags |= mask; + }; + + setFlag("Spells", ESM::NPC::Spells); + setFlag("Spellmaking", ESM::NPC::Spellmaking); + setFlag("Enchanting", ESM::NPC::Enchanting); + setFlag("Training", ESM::NPC::Training); + setFlag("Repair", ESM::NPC::Repair); + setFlag("Barter", ESM::NPC::AllItems); + setFlag("Weapon", ESM::NPC::Weapon); + setFlag("Armor", ESM::NPC::Armor); + setFlag("Clothing", ESM::NPC::Clothing); + setFlag("Books", ESM::NPC::Books); + setFlag("Ingredients", ESM::NPC::Ingredients); + setFlag("Picks", ESM::NPC::Picks); + setFlag("Probes", ESM::NPC::Probes); + setFlag("Lights", ESM::NPC::Lights); + setFlag("Apparatus", ESM::NPC::Apparatus); + setFlag("RepairItem", ESM::NPC::RepairItem); + setFlag("Misc", ESM::NPC::Misc); + setFlag("Potions", ESM::NPC::Potions); + setFlag("MagicItems", ESM::NPC::MagicItems); + + npc.mAiData.mServices = flags; + } + return npc; } } From 64a45f8aeb5c9bbae51b8ffd5ccd71deb76b6bb2 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 16:30:16 -0700 Subject: [PATCH 074/102] Change world store insert --- apps/openmw/mwworld/store.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 80bcdb056a..c088a4ef1f 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -269,15 +269,13 @@ namespace MWWorld list.push_back((*it)->mId); } } - template - T* TypedDynamicStore::insert(const T& item, bool overrideOnly) + template + T* TypedDynamicStore::insert(const T& item, bool /*overrideOnly*/) { - if (overrideOnly) - { - auto it = mStatic.find(item.mId); - if (it == mStatic.end()) - return nullptr; - } + // Check if the ID already exists in static or dynamic stores + auto itStatic = mStatic.find(item.mId); + auto itDynamic = mDynamic.find(item.mId); + std::pair result = mDynamic.insert_or_assign(item.mId, item); T* ptr = &result.first->second; if (result.second) From 5209685783946fac41c7970a7a091cdba7fd50da Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 16:40:30 -0700 Subject: [PATCH 075/102] revert some changes --- apps/openmw/mwworld/esmstore.cpp | 2 +- apps/openmw/mwworld/store.cpp | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 7262805f81..1f41ff3d2c 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -738,9 +738,9 @@ namespace MWWorld case ESM::REC_LEVI: case ESM::REC_LEVC: case ESM::REC_LIGH: + case ESM::REC_NPC_: mStoreImp->mRecNameToStore[type]->read(reader); return true; - case ESM::REC_NPC_: case ESM::REC_CREA: case ESM::REC_CONT: mStoreImp->mRecNameToStore[type]->read(reader, true); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index c088a4ef1f..80bcdb056a 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -269,13 +269,15 @@ namespace MWWorld list.push_back((*it)->mId); } } - template - T* TypedDynamicStore::insert(const T& item, bool /*overrideOnly*/) + template + T* TypedDynamicStore::insert(const T& item, bool overrideOnly) { - // Check if the ID already exists in static or dynamic stores - auto itStatic = mStatic.find(item.mId); - auto itDynamic = mDynamic.find(item.mId); - + if (overrideOnly) + { + auto it = mStatic.find(item.mId); + if (it == mStatic.end()) + return nullptr; + } std::pair result = mDynamic.insert_or_assign(item.mId, item); T* ptr = &result.first->second; if (result.second) From 1fbbaefb477d4f5ea78c9c5e13333859904ff6e2 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 16:45:24 -0700 Subject: [PATCH 076/102] Remove unrelated change, doc --- apps/openmw/mwlua/worldbindings.cpp | 5 ----- files/lua_api/openmw/types.lua | 7 +++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index de8eec3710..495761e46f 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -156,11 +156,6 @@ namespace MWLua MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, cell, count.value_or(1)); return GObject(newPtr); }; - api["advanceTime"] = [context](double hours, bool incremental) { - context.mLuaManager->addAction([ hours, incremental] { - MWBase::Environment::get().getWorld()->advanceTime(hours, incremental); - }); - }; api["getObjectByFormId"] = [](std::string_view formIdStr) -> GObject { ESM::RefId refId = ESM::RefId::deserializeText(formIdStr); if (!refId.is()) diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index b2711c895b..5c21de8f56 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -875,6 +875,13 @@ -- @field #Actor baseType @{#Actor} -- @field [parent=#NPC] #NpcStats stats +--- +-- Creates a @{#NpcRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#NPC] createRecordDraft +-- @param #NpcRecord book A Lua table with the fields of a NpcRecord, with an optional field `template` that accepts a @{#NpcRecord} as a base. +-- @return #NpcRecord A strongly typed NPC record. + --- -- A read-only list of all @{#NpcRecord}s in the world database, may be indexed by recordId. -- Implements [iterables#List](iterables.html#List) of #NpcRecord. From f2cfaac4b882d161e5832dd07d10c0ce33b502eb Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 16:46:25 -0700 Subject: [PATCH 077/102] Fix clang --- apps/openmw/mwlua/worldbindings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index 495761e46f..dca136cd63 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -7,9 +7,9 @@ #include #include #include +#include #include #include -#include #include #include From c139680ce3e79d760e474da220f7fb37510d4847 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 16:49:19 -0700 Subject: [PATCH 078/102] Add line --- apps/openmw/mwlua/types/npc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 705e0831fd..45d600becd 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -255,6 +255,7 @@ namespace MWLua auto& stats = cls.getNpcStats(o.ptr()); stats.setBaseDisposition(stats.getBaseDisposition() + value); }; + npc["createRecordDraft"] = tableToNPC; npc["getFactionRank"] = [](const Object& actor, std::string_view faction) -> size_t { const MWWorld::Ptr ptr = actor.ptr(); From 1986891e7988f2d723281b3b31f27bc19e71d066 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 16:51:10 -0700 Subject: [PATCH 079/102] different empty line? --- apps/openmw/mwlua/types/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 45d600becd..9d5277eb72 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -255,7 +255,7 @@ namespace MWLua auto& stats = cls.getNpcStats(o.ptr()); stats.setBaseDisposition(stats.getBaseDisposition() + value); }; - + npc["createRecordDraft"] = tableToNPC; npc["getFactionRank"] = [](const Object& actor, std::string_view faction) -> size_t { const MWWorld::Ptr ptr = actor.ptr(); From 5203f300e2836c59cf95e05fc0c216fd99eca8eb Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sat, 2 Aug 2025 05:33:27 -0700 Subject: [PATCH 080/102] Requested changes --- apps/openmw/mwlua/types/npc.cpp | 4 ++-- apps/openmw/mwworld/esmstore.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 9d5277eb72..3aa84257bd 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -88,7 +88,7 @@ namespace } if (rec["baseDisposition"] != sol::nil) - npc.mNpdt.mDisposition = static_cast(rec["baseDisposition"]); + npc.mNpdt.mDisposition = rec["baseDisposition"].get(); if (rec["baseGold"] != sol::nil) npc.mNpdt.mGold = static_cast(rec["baseGold"]); @@ -101,7 +101,7 @@ namespace { const sol::table services = rec["servicesOffered"]; int flags = 0; - auto setFlag = [&](const char* key, int mask) { + auto setFlag = [&](const std::string_view& key, int mask) { if (services[key] != sol::nil && services[key]) flags |= mask; }; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 1f41ff3d2c..369731a019 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -726,8 +726,6 @@ namespace MWWorld switch (type) { case ESM::REC_ALCH: - case ESM::REC_MISC: - case ESM::REC_ACTI: case ESM::REC_ARMO: case ESM::REC_BOOK: case ESM::REC_CLAS: @@ -735,14 +733,16 @@ namespace MWWorld case ESM::REC_ENCH: case ESM::REC_SPEL: case ESM::REC_WEAP: + mStoreImp->mRecNameToStore[type]->read(reader); + return true; + case ESM::REC_NPC_: + case ESM::REC_CREA: + case ESM::REC_CONT: + case ESM::REC_MISC: + case ESM::REC_ACTI: case ESM::REC_LEVI: case ESM::REC_LEVC: case ESM::REC_LIGH: - case ESM::REC_NPC_: - mStoreImp->mRecNameToStore[type]->read(reader); - return true; - case ESM::REC_CREA: - case ESM::REC_CONT: mStoreImp->mRecNameToStore[type]->read(reader, true); return true; From 7c665643ad92e53baebb309729a1dd205185344f Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sat, 2 Aug 2025 06:08:29 -0700 Subject: [PATCH 081/102] Use overload --- apps/openmw/mwlua/types/npc.cpp | 4 ++-- apps/openmw/mwworld/store.cpp | 20 ++++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 3aa84257bd..1905ac7c03 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -91,10 +91,10 @@ namespace npc.mNpdt.mDisposition = rec["baseDisposition"].get(); if (rec["baseGold"] != sol::nil) - npc.mNpdt.mGold = static_cast(rec["baseGold"]); + npc.mNpdt.mGold = rec["baseGold"].get(); if (rec["bloodType"] != sol::nil) - npc.mBloodType = static_cast(rec["bloodType"]); + npc.mBloodType = rec["bloodType"].get(); // Services offered if (rec["servicesOffered"] != sol::nil) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 80bcdb056a..0e64e6e627 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -269,13 +269,29 @@ namespace MWWorld list.push_back((*it)->mId); } } + template + inline bool shouldInsert(const IdType& id, const StaticMap& map) + { + auto it = map.find(id); + return it != map.end(); + } + + template + inline bool shouldInsert(const ESM::RefId& id, const StaticMap& map) + { + if (!id.template is()) + { + auto it = map.find(id); + return it != map.end(); + } + return true; + } template T* TypedDynamicStore::insert(const T& item, bool overrideOnly) { if (overrideOnly) { - auto it = mStatic.find(item.mId); - if (it == mStatic.end()) + if (!shouldInsert(item.mId, mStatic)) return nullptr; } std::pair result = mDynamic.insert_or_assign(item.mId, item); From 31abd366ffde3e3cad2ef0d9807e86f87d5f1d24 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sat, 2 Aug 2025 06:25:14 -0700 Subject: [PATCH 082/102] no const --- apps/openmw/mwlua/types/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 1905ac7c03..4cd942d831 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -101,7 +101,7 @@ namespace { const sol::table services = rec["servicesOffered"]; int flags = 0; - auto setFlag = [&](const std::string_view& key, int mask) { + auto setFlag = [&](std::string_view key, int mask) { if (services[key] != sol::nil && services[key]) flags |= mask; }; From d40a78e8ac39fcd62efe63a32047d225b41295d3 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sat, 2 Aug 2025 06:32:46 -0700 Subject: [PATCH 083/102] Player shennanigans --- apps/openmw/mwlua/types/npc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 4cd942d831..220b845882 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -41,8 +41,8 @@ namespace else npc.blank(); - // Force dummy ID - npc.mId = ESM::RefId::deserializeText("blank"); + if (npc.mId == ESM::RefId::deserializeText("Player")) + npc.mId = ESM::RefId::deserializeText("blank"); // Basic fields if (rec["name"] != sol::nil) From 3147aea0edbbc5f1d01bef24bbf138afb9774cc7 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 2 Aug 2025 09:00:59 -0700 Subject: [PATCH 084/102] Better dead zone handling --- apps/openmw/mwgui/race.cpp | 3 +-- apps/openmw/mwinput/controllermanager.cpp | 12 +++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index a4125cfbdd..193c409ab7 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -511,8 +511,7 @@ namespace MWGui { if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTX) { - if (arg.value < -1000 || arg.value > 1000) - onPreviewScroll(nullptr, arg.value < 0 ? 1 : -1); + onPreviewScroll(nullptr, arg.value < 0 ? 1 : -1); return true; } diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index c31fd0f3a5..f32bf741db 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -355,22 +355,25 @@ namespace MWInput MWGui::WindowBase* topWin = winMgr->getActiveControllerWindow(); if (topWin) { - bool isPastDeadzone = std::abs(arg.value) > 2000; // Update cursor state mGamepadGuiCursorEnabled = topWin->isGamepadCursorAllowed(); if (!mGamepadGuiCursorEnabled) winMgr->setCursorActive(false); + // Deadzone check + if (std::abs(arg.value) < 2000) + return !mGamepadGuiCursorEnabled; + if (mGamepadGuiCursorEnabled && (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY)) { // Treat the left stick like a cursor, which is the default behavior. - if (isPastDeadzone && winMgr->getControllerTooltip()) + if (winMgr->getControllerTooltip()) { winMgr->setControllerTooltip(false); winMgr->setCursorVisible(true); } - else if (isPastDeadzone && mGamepadGuiCursorEnabled) + else if (mGamepadGuiCursorEnabled) { winMgr->setCursorVisible(true); } @@ -378,8 +381,7 @@ namespace MWInput } // Some windows have a specific widget to scroll with the right stick. Move the mouse there. - if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTY && isPastDeadzone - && topWin->getControllerScrollWidget() != nullptr) + if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTY && topWin->getControllerScrollWidget() != nullptr) { mMouseManager->warpMouseToWidget(topWin->getControllerScrollWidget()); winMgr->setCursorVisible(false); From 7e9d4795ba5ce83159904e3af74ae111507ed24f Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 2 Aug 2025 09:11:30 -0700 Subject: [PATCH 085/102] Fix range check in journal window --- apps/openmw/mwgui/journalwindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 0d8e8cf4ef..adc05cf5b8 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -754,13 +754,15 @@ namespace { // Choose a quest Gui::MWList* list = getWidget(QuestsList); - notifyQuestClicked(list->getItemNameAt(mSelectedQuest), 0); + if (mSelectedQuest < list->getItemCount()) + notifyQuestClicked(list->getItemNameAt(mSelectedQuest), 0); } else if (mTopicsMode) { // Choose a topic Gui::MWList* list = getWidget(TopicsList); - notifyTopicSelected(list->getItemNameAt(mSelectedQuest), 0); + if (mSelectedQuest < list->getItemCount()) + notifyTopicSelected(list->getItemNameAt(mSelectedQuest), 0); } else { From d7a411cc7214eda1ace3f2be9445e7e844709421 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sun, 3 Aug 2025 11:44:45 -0700 Subject: [PATCH 086/102] Suggested fixes --- apps/openmw/mwlua/types/npc.cpp | 3 +-- apps/openmw/mwlua/worldbindings.cpp | 6 +++++- apps/openmw/mwworld/store.cpp | 15 ++++----------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 220b845882..2f6b0fa0ba 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -41,8 +41,7 @@ namespace else npc.blank(); - if (npc.mId == ESM::RefId::deserializeText("Player")) - npc.mId = ESM::RefId::deserializeText("blank"); + npc.mId = {}; // Basic fields if (rec["name"] != sol::nil) diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index dca136cd63..228e70b464 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -191,7 +191,11 @@ namespace MWLua }, [lua = context.mLua](const ESM::NPC& npc) -> const ESM::NPC* { checkGameInitialized(lua); - return MWBase::Environment::get().getESMStore()->insert(npc); + if (npc.mId.empty()) + return MWBase::Environment::get().getESMStore()->insert(npc); + ESM::NPC copy = npc; + copy.mId = {}; + return MWBase::Environment::get().getESMStore()->insert(copy); }, [lua = context.mLua](const ESM::Weapon& weapon) -> const ESM::Weapon* { checkGameInitialized(lua); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 0e64e6e627..870c2a60c9 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -276,22 +276,15 @@ namespace MWWorld return it != map.end(); } - template - inline bool shouldInsert(const ESM::RefId& id, const StaticMap& map) - { - if (!id.template is()) - { - auto it = map.find(id); - return it != map.end(); - } - return true; - } template T* TypedDynamicStore::insert(const T& item, bool overrideOnly) { + if constexpr (std::is_same_v) + overrideOnly = overrideOnly && !item.mId.template is(); if (overrideOnly) { - if (!shouldInsert(item.mId, mStatic)) + auto it = mStatic.find(item.mId); + if (it == mStatic.end()) return nullptr; } std::pair result = mDynamic.insert_or_assign(item.mId, item); From a0585949a984105d592d83d59e3c536b0fbbd253 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sun, 3 Aug 2025 12:50:57 -0700 Subject: [PATCH 087/102] Remove dead code, fix grammer --- apps/openmw/mwworld/store.cpp | 6 ------ files/lua_api/openmw/types.lua | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 870c2a60c9..12c45e947d 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -269,12 +269,6 @@ namespace MWWorld list.push_back((*it)->mId); } } - template - inline bool shouldInsert(const IdType& id, const StaticMap& map) - { - auto it = map.find(id); - return it != map.end(); - } template T* TypedDynamicStore::insert(const T& item, bool overrideOnly) diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index e85da3f0f6..6e1bee5ab4 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -876,10 +876,10 @@ -- @field [parent=#NPC] #NpcStats stats --- --- Creates a @{#NpcRecord} without adding it to the world database. +-- Creates an @{#NpcRecord} without adding it to the world database. -- Use @{openmw_world#(world).createRecord} to add the record to the world. -- @function [parent=#NPC] createRecordDraft --- @param #NpcRecord book A Lua table with the fields of a NpcRecord, with an optional field `template` that accepts a @{#NpcRecord} as a base. +-- @param #NpcRecord npc A Lua table with the fields of an NpcRecord, with an optional field `template` that accepts an @{#NpcRecord} as a base. -- @return #NpcRecord A strongly typed NPC record. --- From 99ae52e94ca1443bee0bb16d918de164cd5e620f Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sun, 3 Aug 2025 13:42:04 -0700 Subject: [PATCH 088/102] Add fields to record --- apps/openmw/mwlua/types/actor.hpp | 13 ++--- apps/openmw/mwlua/types/npc.cpp | 56 ++++++++++++--------- apps/openmw/mwlua/types/servicesoffered.hpp | 26 ++++++++++ files/lua_api/openmw/types.lua | 3 ++ 4 files changed, 64 insertions(+), 34 deletions(-) create mode 100644 apps/openmw/mwlua/types/servicesoffered.hpp diff --git a/apps/openmw/mwlua/types/actor.hpp b/apps/openmw/mwlua/types/actor.hpp index 425e44451b..f55089c5a4 100644 --- a/apps/openmw/mwlua/types/actor.hpp +++ b/apps/openmw/mwlua/types/actor.hpp @@ -16,6 +16,7 @@ #include "../context.hpp" +#include "servicesoffered.hpp" namespace MWLua { @@ -25,15 +26,6 @@ namespace MWLua record["servicesOffered"] = sol::readonly_property([context](const T& rec) -> sol::table { sol::state_view lua = context.sol(); sol::table providedServices(lua, sol::create); - constexpr std::array, 19> serviceNames = { { { ESM::NPC::Spells, - "Spells" }, - { ESM::NPC::Spellmaking, "Spellmaking" }, { ESM::NPC::Enchanting, "Enchanting" }, - { ESM::NPC::Training, "Training" }, { ESM::NPC::Repair, "Repair" }, { ESM::NPC::AllItems, "Barter" }, - { ESM::NPC::Weapon, "Weapon" }, { ESM::NPC::Armor, "Armor" }, { ESM::NPC::Clothing, "Clothing" }, - { ESM::NPC::Books, "Books" }, { ESM::NPC::Ingredients, "Ingredients" }, { ESM::NPC::Picks, "Picks" }, - { ESM::NPC::Probes, "Probes" }, { ESM::NPC::Lights, "Lights" }, { ESM::NPC::Apparatus, "Apparatus" }, - { ESM::NPC::RepairItem, "RepairItem" }, { ESM::NPC::Misc, "Misc" }, { ESM::NPC::Potions, "Potions" }, - { ESM::NPC::MagicItems, "MagicItems" } } }; int services = rec.mAiData.mServices; if constexpr (std::is_same_v) @@ -42,10 +34,11 @@ namespace MWLua services = MWBase::Environment::get().getESMStore()->get().find(rec.mClass)->mData.mServices; } - for (const auto& [flag, name] : serviceNames) + for (const auto& [flag, name] : ServiceNames) { providedServices[name] = (services & flag) != 0; } + providedServices["Travel"] = !rec.getTransport().empty(); return LuaUtil::makeReadOnly(providedServices); }); diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 2f6b0fa0ba..c0ffce9402 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -2,6 +2,7 @@ #include "actor.hpp" #include "modelproperty.hpp" +#include "servicesoffered.hpp" #include #include @@ -58,6 +59,8 @@ namespace npc.mHead = ESM::RefId::deserializeText(rec["head"].get()); if (rec["hair"] != sol::nil) npc.mHair = ESM::RefId::deserializeText(rec["hair"].get()); + if (rec["primaryFaction"] != sol::nil) + npc.mFaction = ESM::RefId::deserializeText(rec["primaryFaction"].get()); if (rec["isMale"] != sol::nil) { @@ -77,6 +80,15 @@ namespace npc.mFlags &= ~ESM::NPC::Essential; } + if (rec["autocalc"] != sol::nil) + { + bool respawn = rec["autocalc"]; + if (respawn) + npc.mFlags |= ESM::NPC::Autocalc; + else + npc.mFlags &= ~ESM::NPC::Autocalc; + } + if (rec["isRespawning"] != sol::nil) { bool respawn = rec["isRespawning"]; @@ -95,35 +107,20 @@ namespace if (rec["bloodType"] != sol::nil) npc.mBloodType = rec["bloodType"].get(); - // Services offered + if (rec["primaryFactionRank"] != sol::nil) + npc.mNpdt.mRank = rec["primaryFactionRank"].get(); + if (rec["servicesOffered"] != sol::nil) { const sol::table services = rec["servicesOffered"]; int flags = 0; - auto setFlag = [&](std::string_view key, int mask) { - if (services[key] != sol::nil && services[key]) - flags |= mask; - }; - setFlag("Spells", ESM::NPC::Spells); - setFlag("Spellmaking", ESM::NPC::Spellmaking); - setFlag("Enchanting", ESM::NPC::Enchanting); - setFlag("Training", ESM::NPC::Training); - setFlag("Repair", ESM::NPC::Repair); - setFlag("Barter", ESM::NPC::AllItems); - setFlag("Weapon", ESM::NPC::Weapon); - setFlag("Armor", ESM::NPC::Armor); - setFlag("Clothing", ESM::NPC::Clothing); - setFlag("Books", ESM::NPC::Books); - setFlag("Ingredients", ESM::NPC::Ingredients); - setFlag("Picks", ESM::NPC::Picks); - setFlag("Probes", ESM::NPC::Probes); - setFlag("Lights", ESM::NPC::Lights); - setFlag("Apparatus", ESM::NPC::Apparatus); - setFlag("RepairItem", ESM::NPC::RepairItem); - setFlag("Misc", ESM::NPC::Misc); - setFlag("Potions", ESM::NPC::Potions); - setFlag("MagicItems", ESM::NPC::MagicItems); + for (const auto& [mask, key] : ServiceNames) + { + sol::object value = services[key]; + if (value != sol::nil && value.as()) + flags |= mask; + } npc.mAiData.mServices = flags; } @@ -198,9 +195,20 @@ namespace MWLua = sol::readonly_property([](const ESM::NPC& rec) -> int { return (int)rec.mNpdt.mDisposition; }); record["head"] = sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHead.serializeText(); }); + record["primaryFaction"] = sol::readonly_property( + [](const ESM::NPC& rec) -> sol::optional { return LuaUtil::serializeRefId(rec.mFaction); }); + record["primaryFactionRank"] + = sol::readonly_property([](const ESM::NPC& rec, sol::this_state s) -> sol::object { + sol::state_view lua(s); + if (rec.mFaction.empty()) + return sol::make_object(lua, sol::nil); // return nil + return sol::make_object(lua, rec.mNpdt.mRank); // return the rank as a number + }); addModelProperty(record); record["isEssential"] = sol::readonly_property([](const ESM::NPC& rec) -> bool { return rec.mFlags & ESM::NPC::Essential; }); + record["autocalc"] + = sol::readonly_property([](const ESM::NPC& rec) -> bool { return rec.mFlags & ESM::NPC::Autocalc; }); record["isMale"] = sol::readonly_property([](const ESM::NPC& rec) -> bool { return rec.isMale(); }); record["isRespawning"] = sol::readonly_property([](const ESM::NPC& rec) -> bool { return rec.mFlags & ESM::NPC::Respawn; }); diff --git a/apps/openmw/mwlua/types/servicesoffered.hpp b/apps/openmw/mwlua/types/servicesoffered.hpp new file mode 100644 index 0000000000..c854baca4d --- /dev/null +++ b/apps/openmw/mwlua/types/servicesoffered.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include // for ESM::NPC constants + +inline constexpr std::array, 19> ServiceNames = {{ + { ESM::NPC::Spells, "Spells" }, + { ESM::NPC::Spellmaking, "Spellmaking" }, + { ESM::NPC::Enchanting, "Enchanting" }, + { ESM::NPC::Training, "Training" }, + { ESM::NPC::Repair, "Repair" }, + { ESM::NPC::AllItems, "Barter" }, + { ESM::NPC::Weapon, "Weapon" }, + { ESM::NPC::Armor, "Armor" }, + { ESM::NPC::Clothing, "Clothing" }, + { ESM::NPC::Books, "Books" }, + { ESM::NPC::Ingredients, "Ingredients" }, + { ESM::NPC::Picks, "Picks" }, + { ESM::NPC::Probes, "Probes" }, + { ESM::NPC::Lights, "Lights" }, + { ESM::NPC::Apparatus, "Apparatus" }, + { ESM::NPC::RepairItem, "RepairItem" }, + { ESM::NPC::Misc, "Misc" }, + { ESM::NPC::Potions, "Potions" }, + { ESM::NPC::MagicItems, "MagicItems" } +}}; \ No newline at end of file diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 6e1bee5ab4..5f78467bdc 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -861,6 +861,9 @@ -- @field #boolean canWalk whether the creature can walk -- @field #boolean canUseWeapons whether the creature can use weapons and shields -- @field #boolean isBiped whether the creature is a biped +-- @field #boolean autocalc If true, the actors stats will be automatically calculated based on level and class. +-- @field #string primaryFaction Faction ID of the NPCs default faction. Nil if no faction +-- @field #number primaryFactionRank Faction rank of the NPCs default faction. Nil if no faction -- @field #boolean isEssential whether the creature is essential -- @field #boolean isRespawning whether the creature respawns after death -- @field #number bloodType integer representing the blood type of the Creature. Used to generate the correct blood vfx. From afb7f1da54feab991f95c97656a0b27b9b0ddb62 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sun, 3 Aug 2025 21:39:44 -0700 Subject: [PATCH 089/102] Fix formatting --- apps/openmw/mwlua/types/servicesoffered.hpp | 31 ++++++--------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwlua/types/servicesoffered.hpp b/apps/openmw/mwlua/types/servicesoffered.hpp index c854baca4d..acf928dc7c 100644 --- a/apps/openmw/mwlua/types/servicesoffered.hpp +++ b/apps/openmw/mwlua/types/servicesoffered.hpp @@ -1,26 +1,13 @@ #pragma once #include +#include // for ESM::NPC constants #include -#include // for ESM::NPC constants -inline constexpr std::array, 19> ServiceNames = {{ - { ESM::NPC::Spells, "Spells" }, - { ESM::NPC::Spellmaking, "Spellmaking" }, - { ESM::NPC::Enchanting, "Enchanting" }, - { ESM::NPC::Training, "Training" }, - { ESM::NPC::Repair, "Repair" }, - { ESM::NPC::AllItems, "Barter" }, - { ESM::NPC::Weapon, "Weapon" }, - { ESM::NPC::Armor, "Armor" }, - { ESM::NPC::Clothing, "Clothing" }, - { ESM::NPC::Books, "Books" }, - { ESM::NPC::Ingredients, "Ingredients" }, - { ESM::NPC::Picks, "Picks" }, - { ESM::NPC::Probes, "Probes" }, - { ESM::NPC::Lights, "Lights" }, - { ESM::NPC::Apparatus, "Apparatus" }, - { ESM::NPC::RepairItem, "RepairItem" }, - { ESM::NPC::Misc, "Misc" }, - { ESM::NPC::Potions, "Potions" }, - { ESM::NPC::MagicItems, "MagicItems" } -}}; \ No newline at end of file +inline constexpr std::array, 19> ServiceNames + = { { { ESM::NPC::Spells, "Spells" }, { ESM::NPC::Spellmaking, "Spellmaking" }, + { ESM::NPC::Enchanting, "Enchanting" }, { ESM::NPC::Training, "Training" }, { ESM::NPC::Repair, "Repair" }, + { ESM::NPC::AllItems, "Barter" }, { ESM::NPC::Weapon, "Weapon" }, { ESM::NPC::Armor, "Armor" }, + { ESM::NPC::Clothing, "Clothing" }, { ESM::NPC::Books, "Books" }, { ESM::NPC::Ingredients, "Ingredients" }, + { ESM::NPC::Picks, "Picks" }, { ESM::NPC::Probes, "Probes" }, { ESM::NPC::Lights, "Lights" }, + { ESM::NPC::Apparatus, "Apparatus" }, { ESM::NPC::RepairItem, "RepairItem" }, { ESM::NPC::Misc, "Misc" }, + { ESM::NPC::Potions, "Potions" }, { ESM::NPC::MagicItems, "MagicItems" } } }; From b2746a7b4ba248b27173b2291025e060ea3b4887 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Mon, 4 Aug 2025 08:27:26 -0700 Subject: [PATCH 090/102] Fix autocalc, remove thing --- apps/openmw/mwlua/types/npc.cpp | 8 ++++---- apps/openmw/mwlua/types/servicesoffered.hpp | 3 +-- files/lua_api/openmw/types.lua | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index c0ffce9402..49acfb111b 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -80,10 +80,10 @@ namespace npc.mFlags &= ~ESM::NPC::Essential; } - if (rec["autocalc"] != sol::nil) + if (rec["isAutocalc"] != sol::nil) { - bool respawn = rec["autocalc"]; - if (respawn) + bool autoCalc = rec["isAutocalc"]; + if (autoCalc) npc.mFlags |= ESM::NPC::Autocalc; else npc.mFlags &= ~ESM::NPC::Autocalc; @@ -207,7 +207,7 @@ namespace MWLua addModelProperty(record); record["isEssential"] = sol::readonly_property([](const ESM::NPC& rec) -> bool { return rec.mFlags & ESM::NPC::Essential; }); - record["autocalc"] + record["isAutocalc"] = sol::readonly_property([](const ESM::NPC& rec) -> bool { return rec.mFlags & ESM::NPC::Autocalc; }); record["isMale"] = sol::readonly_property([](const ESM::NPC& rec) -> bool { return rec.isMale(); }); record["isRespawning"] diff --git a/apps/openmw/mwlua/types/servicesoffered.hpp b/apps/openmw/mwlua/types/servicesoffered.hpp index acf928dc7c..bfc4f96fc9 100644 --- a/apps/openmw/mwlua/types/servicesoffered.hpp +++ b/apps/openmw/mwlua/types/servicesoffered.hpp @@ -1,6 +1,5 @@ -#pragma once #include -#include // for ESM::NPC constants +#include #include inline constexpr std::array, 19> ServiceNames diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 5f78467bdc..aa5b70c4e0 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -861,7 +861,7 @@ -- @field #boolean canWalk whether the creature can walk -- @field #boolean canUseWeapons whether the creature can use weapons and shields -- @field #boolean isBiped whether the creature is a biped --- @field #boolean autocalc If true, the actors stats will be automatically calculated based on level and class. +-- @field #boolean isAutocalc If true, the actors stats will be automatically calculated based on level and class. -- @field #string primaryFaction Faction ID of the NPCs default faction. Nil if no faction -- @field #number primaryFactionRank Faction rank of the NPCs default faction. Nil if no faction -- @field #boolean isEssential whether the creature is essential From 2ab87f2d2241fb98fffc1a07be4edd84026bbe9c Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Mon, 4 Aug 2025 08:35:13 -0700 Subject: [PATCH 091/102] Use namespace for servicesoffered --- apps/openmw/mwlua/types/actor.hpp | 2 +- apps/openmw/mwlua/types/npc.cpp | 2 +- apps/openmw/mwlua/types/servicesoffered.hpp | 26 ++++++++++++++------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwlua/types/actor.hpp b/apps/openmw/mwlua/types/actor.hpp index f55089c5a4..bd098d99d5 100644 --- a/apps/openmw/mwlua/types/actor.hpp +++ b/apps/openmw/mwlua/types/actor.hpp @@ -34,7 +34,7 @@ namespace MWLua services = MWBase::Environment::get().getESMStore()->get().find(rec.mClass)->mData.mServices; } - for (const auto& [flag, name] : ServiceNames) + for (const auto& [flag, name] : MWLua::ServiceNames) { providedServices[name] = (services & flag) != 0; } diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 49acfb111b..489b01e140 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -115,7 +115,7 @@ namespace const sol::table services = rec["servicesOffered"]; int flags = 0; - for (const auto& [mask, key] : ServiceNames) + for (const auto& [mask, key] : MWLua::ServiceNames) { sol::object value = services[key]; if (value != sol::nil && value.as()) diff --git a/apps/openmw/mwlua/types/servicesoffered.hpp b/apps/openmw/mwlua/types/servicesoffered.hpp index bfc4f96fc9..4a87975a7d 100644 --- a/apps/openmw/mwlua/types/servicesoffered.hpp +++ b/apps/openmw/mwlua/types/servicesoffered.hpp @@ -1,12 +1,22 @@ +#ifndef MWLUA_SERVICESOFFERED_HPP +#define MWLUA_SERVICESOFFERED_HPP + #include #include #include -inline constexpr std::array, 19> ServiceNames - = { { { ESM::NPC::Spells, "Spells" }, { ESM::NPC::Spellmaking, "Spellmaking" }, - { ESM::NPC::Enchanting, "Enchanting" }, { ESM::NPC::Training, "Training" }, { ESM::NPC::Repair, "Repair" }, - { ESM::NPC::AllItems, "Barter" }, { ESM::NPC::Weapon, "Weapon" }, { ESM::NPC::Armor, "Armor" }, - { ESM::NPC::Clothing, "Clothing" }, { ESM::NPC::Books, "Books" }, { ESM::NPC::Ingredients, "Ingredients" }, - { ESM::NPC::Picks, "Picks" }, { ESM::NPC::Probes, "Probes" }, { ESM::NPC::Lights, "Lights" }, - { ESM::NPC::Apparatus, "Apparatus" }, { ESM::NPC::RepairItem, "RepairItem" }, { ESM::NPC::Misc, "Misc" }, - { ESM::NPC::Potions, "Potions" }, { ESM::NPC::MagicItems, "MagicItems" } } }; +namespace MWLua +{ + + inline constexpr std::array, 19> ServiceNames + = { { { ESM::NPC::Spells, "Spells" }, { ESM::NPC::Spellmaking, "Spellmaking" }, + { ESM::NPC::Enchanting, "Enchanting" }, { ESM::NPC::Training, "Training" }, { ESM::NPC::Repair, "Repair" }, + { ESM::NPC::AllItems, "Barter" }, { ESM::NPC::Weapon, "Weapon" }, { ESM::NPC::Armor, "Armor" }, + { ESM::NPC::Clothing, "Clothing" }, { ESM::NPC::Books, "Books" }, { ESM::NPC::Ingredients, "Ingredients" }, + { ESM::NPC::Picks, "Picks" }, { ESM::NPC::Probes, "Probes" }, { ESM::NPC::Lights, "Lights" }, + { ESM::NPC::Apparatus, "Apparatus" }, { ESM::NPC::RepairItem, "RepairItem" }, { ESM::NPC::Misc, "Misc" }, + { ESM::NPC::Potions, "Potions" }, { ESM::NPC::MagicItems, "MagicItems" } } }; +} + + +#endif \ No newline at end of file From 4c5118a24bef8e388ddcb23d2303a4b7d91b7087 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Mon, 4 Aug 2025 08:43:37 -0700 Subject: [PATCH 092/102] Fix rank --- apps/openmw/mwlua/types/npc.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 489b01e140..71e2625063 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -108,7 +108,10 @@ namespace npc.mBloodType = rec["bloodType"].get(); if (rec["primaryFactionRank"] != sol::nil) - npc.mNpdt.mRank = rec["primaryFactionRank"].get(); + { + if (!npc.mFaction.empty()) + npc.mNpdt.mRank = LuaUtil::fromLuaIndex(rec["primaryFactionRank"]); + } if (rec["servicesOffered"] != sol::nil) { @@ -197,13 +200,12 @@ namespace MWLua = sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHead.serializeText(); }); record["primaryFaction"] = sol::readonly_property( [](const ESM::NPC& rec) -> sol::optional { return LuaUtil::serializeRefId(rec.mFaction); }); - record["primaryFactionRank"] - = sol::readonly_property([](const ESM::NPC& rec, sol::this_state s) -> sol::object { - sol::state_view lua(s); - if (rec.mFaction.empty()) - return sol::make_object(lua, sol::nil); // return nil - return sol::make_object(lua, rec.mNpdt.mRank); // return the rank as a number - }); + record["primaryFactionRank"] = sol::readonly_property([](const ESM::NPC& rec, sol::this_state s) -> int { + sol::state_view lua(s); + if (rec.mFaction.empty()) + return 0; + return LuaUtil::toLuaIndex(rec.mNpdt.mRank); + }); addModelProperty(record); record["isEssential"] = sol::readonly_property([](const ESM::NPC& rec) -> bool { return rec.mFlags & ESM::NPC::Essential; }); From aa545467fd5382005f54c3c78ec4259a90e4539c Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Mon, 4 Aug 2025 09:56:58 -0700 Subject: [PATCH 093/102] Remove unused --- apps/openmw/mwlua/types/npc.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 71e2625063..6d26b2409c 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -201,7 +201,6 @@ namespace MWLua record["primaryFaction"] = sol::readonly_property( [](const ESM::NPC& rec) -> sol::optional { return LuaUtil::serializeRefId(rec.mFaction); }); record["primaryFactionRank"] = sol::readonly_property([](const ESM::NPC& rec, sol::this_state s) -> int { - sol::state_view lua(s); if (rec.mFaction.empty()) return 0; return LuaUtil::toLuaIndex(rec.mNpdt.mRank); From 7a5fe778bcd2729bf608a38faf4d882ee1aff307 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Mon, 4 Aug 2025 10:04:34 -0700 Subject: [PATCH 094/102] Add validation --- apps/openmw/mwlua/types/npc.cpp | 53 +++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 6d26b2409c..92e147a4d2 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -32,6 +32,19 @@ namespace sol namespace { + size_t getValidRanksCount(const ESM::Faction* faction) + { + if (!faction) + return 0; + + for (size_t i = 0; i < faction->mRanks.size(); i++) + { + if (faction->mRanks[i].empty()) + return i; + } + + return faction->mRanks.size(); + } ESM::NPC tableToNPC(const sol::table& rec) { ESM::NPC npc; @@ -60,8 +73,16 @@ namespace if (rec["hair"] != sol::nil) npc.mHair = ESM::RefId::deserializeText(rec["hair"].get()); if (rec["primaryFaction"] != sol::nil) - npc.mFaction = ESM::RefId::deserializeText(rec["primaryFaction"].get()); + { + auto factionStr = rec["primaryFaction"].get(); + ESM::RefId factionId = ESM::RefId::deserializeText(factionStr); + const auto& factionStore = MWBase::Environment::get().getESMStore()->get(); + if (!factionStore.search(factionId)) + throw std::runtime_error("Invalid faction '" + std::string(factionStr) + "' in primaryFaction"); + + npc.mFaction = factionId; + } if (rec["isMale"] != sol::nil) { bool male = rec["isMale"]; @@ -110,7 +131,22 @@ namespace if (rec["primaryFactionRank"] != sol::nil) { if (!npc.mFaction.empty()) - npc.mNpdt.mRank = LuaUtil::fromLuaIndex(rec["primaryFactionRank"]); + { + const ESM::RefId factionId = npc.mFaction; + const ESM::Faction* faction + = MWBase::Environment::get().getESMStore()->get().find(factionId); + + int luaValue = rec["primaryFactionRank"]; + int rank = LuaUtil::fromLuaIndex(luaValue); + + int maxRank = static_cast(getValidRanksCount(faction)); + + if (rank < 0 || rank >= maxRank) + throw std::runtime_error("primaryFactionRank: Requested rank " + std::to_string(rank) + + " is out of bounds for faction " + factionId.toDebugString()); + + npc.mNpdt.mRank = rank; + } } if (rec["servicesOffered"] != sol::nil) @@ -134,19 +170,6 @@ namespace namespace { - size_t getValidRanksCount(const ESM::Faction* faction) - { - if (!faction) - return 0; - - for (size_t i = 0; i < faction->mRanks.size(); i++) - { - if (faction->mRanks[i].empty()) - return i; - } - - return faction->mRanks.size(); - } ESM::RefId parseFactionId(std::string_view faction) { From 1a886db951622bb1901ae56f7f26a5fea1827d68 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Mon, 4 Aug 2025 10:41:31 -0700 Subject: [PATCH 095/102] Merge namespace, remove redundant --- apps/openmw/mwlua/types/npc.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 92e147a4d2..f38f027d2a 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -74,14 +74,13 @@ namespace npc.mHair = ESM::RefId::deserializeText(rec["hair"].get()); if (rec["primaryFaction"] != sol::nil) { - auto factionStr = rec["primaryFaction"].get(); - ESM::RefId factionId = ESM::RefId::deserializeText(factionStr); - + const auto str = rec["primaryFaction"].get(); const auto& factionStore = MWBase::Environment::get().getESMStore()->get(); - if (!factionStore.search(factionId)) - throw std::runtime_error("Invalid faction '" + std::string(factionStr) + "' in primaryFaction"); - npc.mFaction = factionId; + if (!factionStore.search(ESM::RefId::deserializeText(str))) + throw std::runtime_error("Invalid faction '" + str + "' in primaryFaction"); + + npc.mFaction = ESM::RefId::deserializeText(str); } if (rec["isMale"] != sol::nil) { @@ -166,10 +165,6 @@ namespace return npc; } -} - -namespace -{ ESM::RefId parseFactionId(std::string_view faction) { From e74167c7b768a9e621eb332aa9c9d22cb1f149be Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Mon, 4 Aug 2025 11:09:26 -0700 Subject: [PATCH 096/102] Revert bad change, do actual fix --- apps/openmw/mwlua/types/npc.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index f38f027d2a..e847357bd9 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -74,13 +74,14 @@ namespace npc.mHair = ESM::RefId::deserializeText(rec["hair"].get()); if (rec["primaryFaction"] != sol::nil) { - const auto str = rec["primaryFaction"].get(); + auto factionStr = rec["primaryFaction"].get(); + ESM::RefId factionId = ESM::RefId::deserializeText(factionStr); + const auto& factionStore = MWBase::Environment::get().getESMStore()->get(); + if (!factionStore.search(factionId)) + throw std::runtime_error("Invalid faction '" + std::string(factionStr) + "' in primaryFaction"); - if (!factionStore.search(ESM::RefId::deserializeText(str))) - throw std::runtime_error("Invalid faction '" + str + "' in primaryFaction"); - - npc.mFaction = ESM::RefId::deserializeText(str); + npc.mFaction = factionId; } if (rec["isMale"] != sol::nil) { @@ -131,9 +132,8 @@ namespace { if (!npc.mFaction.empty()) { - const ESM::RefId factionId = npc.mFaction; const ESM::Faction* faction - = MWBase::Environment::get().getESMStore()->get().find(factionId); + = MWBase::Environment::get().getESMStore()->get().find(npc.mFaction); int luaValue = rec["primaryFactionRank"]; int rank = LuaUtil::fromLuaIndex(luaValue); @@ -142,7 +142,7 @@ namespace if (rank < 0 || rank >= maxRank) throw std::runtime_error("primaryFactionRank: Requested rank " + std::to_string(rank) - + " is out of bounds for faction " + factionId.toDebugString()); + + " is out of bounds for faction " + npc.mFaction.toDebugString()); npc.mNpdt.mRank = rank; } From 493f70a6614584dfd1d555a7e21fe9e7d8626719 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Mon, 4 Aug 2025 11:10:06 -0700 Subject: [PATCH 097/102] Formatting --- apps/openmw/mwlua/types/servicesoffered.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwlua/types/servicesoffered.hpp b/apps/openmw/mwlua/types/servicesoffered.hpp index 4a87975a7d..94695c44fb 100644 --- a/apps/openmw/mwlua/types/servicesoffered.hpp +++ b/apps/openmw/mwlua/types/servicesoffered.hpp @@ -7,7 +7,7 @@ namespace MWLua { - + inline constexpr std::array, 19> ServiceNames = { { { ESM::NPC::Spells, "Spells" }, { ESM::NPC::Spellmaking, "Spellmaking" }, { ESM::NPC::Enchanting, "Enchanting" }, { ESM::NPC::Training, "Training" }, { ESM::NPC::Repair, "Repair" }, @@ -18,5 +18,4 @@ namespace MWLua { ESM::NPC::Potions, "Potions" }, { ESM::NPC::MagicItems, "MagicItems" } } }; } - -#endif \ No newline at end of file +#endif From ef2332eb373a71e819662649bcdf43107f9f5860 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Mon, 4 Aug 2025 11:30:47 -0700 Subject: [PATCH 098/102] Update API, feature --- CHANGELOG.md | 1 + CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2df2e025e..e3b281b909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,7 @@ Feature #8580: Sort characters in the save loading menu Feature #8597: Lua: Add more built-in event handlers Feature #8629: Expose path grid data to Lua + Feature #8654: Allow lua world.createRecord to create NPC records 0.49.0 ------ diff --git a/CMakeLists.txt b/CMakeLists.txt index 57ebeefcfd..5991c57c4c 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 87) +set(OPENMW_LUA_API_REVISION 88) set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "") From c293c76bd71cb1ab843ef00e7afda963d06cb5b4 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 4 Aug 2025 19:10:07 -0700 Subject: [PATCH 099/102] Remove hardcoded row/column values in some controller menus --- apps/openmw/mwgui/class.cpp | 53 +++++++++++++++++++++-------- apps/openmw/mwgui/class.hpp | 3 ++ apps/openmw/mwgui/levelupdialog.cpp | 23 +++++-------- apps/openmw/mwgui/levelupdialog.hpp | 1 + 4 files changed, 52 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index efd2872408..9573d22377 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -1047,6 +1047,7 @@ namespace MWGui skillWidget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); ToolTips::createSkillToolTip(skillWidget, skill.mId); mSkillButtons.emplace_back(skillWidget); + mNumSkillsPerSpecialization[skill.mData.mSpecialization]++; } for (const auto& [widget, coord] : specializations) { @@ -1115,28 +1116,52 @@ namespace MWGui mControllerFocus = wrap(mControllerFocus + 1, mSkillButtons.size()); mSkillButtons[mControllerFocus]->setStateSelected(true); } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { mSkillButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus < 9) - mControllerFocus += 18; - else - mControllerFocus -= 9; - mSkillButtons[mControllerFocus]->setStateSelected(true); - } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) - { - mSkillButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus >= 18) - mControllerFocus -= 18; - else - mControllerFocus += 9; + selectNextColumn(arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT ? -1 : 1); mSkillButtons[mControllerFocus]->setStateSelected(true); } return true; } + void SelectSkillDialog::selectNextColumn(int direction) + { + // Find which column (specialization) the current index is in. + size_t specialization = 0; + size_t nextSpecializationIndex = 0; + for (; specialization < mNumSkillsPerSpecialization.size(); ++specialization) + { + nextSpecializationIndex += mNumSkillsPerSpecialization[specialization]; + if (mControllerFocus < nextSpecializationIndex) + break; + } + + if (direction < 0) + { + if (mControllerFocus < mNumSkillsPerSpecialization[0]) + { + // Wrap around to the right column + for (size_t i = 0; i < mNumSkillsPerSpecialization.size() - 1; ++i) + mControllerFocus += mNumSkillsPerSpecialization[i]; + } + else + mControllerFocus -= mNumSkillsPerSpecialization[specialization]; + } + else + { + if (mControllerFocus + mNumSkillsPerSpecialization.back() >= mSkillButtons.size()) + { + // Wrap around to the left column + for (size_t i = 0; i < mNumSkillsPerSpecialization.size() - 1; ++i) + mControllerFocus -= mNumSkillsPerSpecialization[i]; + } + else + mControllerFocus += mNumSkillsPerSpecialization[specialization]; + } + } + /* DescriptionDialog */ DescriptionDialog::DescriptionDialog() diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 2f394af9c0..5a769e15bd 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -257,6 +257,9 @@ namespace MWGui private: ESM::RefId mSkillId; + std::array mNumSkillsPerSpecialization; + + void selectNextColumn(int direction); }; class DescriptionDialog : public WindowModal diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 6186401d0a..708fc7b02f 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -49,13 +49,12 @@ namespace MWGui { const auto& store = MWBase::Environment::get().getESMStore()->get(); - const size_t perCol - = static_cast(std::ceil(store.getSize() / static_cast(std::size(sColumnOffsets)))); + mPerCol = static_cast(std::ceil(store.getSize() / static_cast(std::size(sColumnOffsets)))); size_t i = 0; for (const ESM::Attribute& attribute : store) { - const int offset = sColumnOffsets[i / perCol]; - const int row = static_cast(i % perCol); + const int offset = sColumnOffsets[i / mPerCol]; + const int row = static_cast(i % mPerCol); Widgets widgets; widgets.mMultiplier = mAssignWidget->createWidget( "SandTextVCenter", { offset, 20 * row, 100, 20 }, MyGUI::Align::Default); @@ -79,7 +78,7 @@ namespace MWGui mAssignWidget->setVisibleVScroll(false); mAssignWidget->setCanvasSize(MyGUI::IntSize( - mAssignWidget->getWidth(), std::max(mAssignWidget->getHeight(), static_cast(20 * perCol)))); + mAssignWidget->getWidth(), std::max(mAssignWidget->getHeight(), static_cast(20 * mPerCol)))); mAssignWidget->setVisibleVScroll(true); mAssignWidget->setViewOffset(MyGUI::IntPoint()); } @@ -397,10 +396,8 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { setControllerFocus(mAttributeButtons, mControllerFocus, false); - if (mControllerFocus == 0) - mControllerFocus = 3; - else if (mControllerFocus == 4) - mControllerFocus = 7; + if (mControllerFocus % mPerCol == 0) + mControllerFocus += mPerCol - 1; else mControllerFocus--; setControllerFocus(mAttributeButtons, mControllerFocus, true); @@ -408,10 +405,8 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { setControllerFocus(mAttributeButtons, mControllerFocus, false); - if (mControllerFocus == 3) - mControllerFocus = 0; - else if (mControllerFocus == 7) - mControllerFocus = 4; + if (mControllerFocus % mPerCol == mPerCol - 1) + mControllerFocus -= mPerCol - 1; else mControllerFocus++; setControllerFocus(mAttributeButtons, mControllerFocus, true); @@ -419,7 +414,7 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { setControllerFocus(mAttributeButtons, mControllerFocus, false); - mControllerFocus = (mControllerFocus + 4) % mAttributeButtons.size(); + mControllerFocus = (mControllerFocus + mPerCol) % mAttributeButtons.size(); setControllerFocus(mAttributeButtons, mControllerFocus, true); } diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp index 05048b3e8f..70bd2839f2 100644 --- a/apps/openmw/mwgui/levelupdialog.hpp +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -37,6 +37,7 @@ namespace MWGui std::vector mSpentAttributes; + size_t mPerCol; unsigned int mCoinCount; void onOkButtonClicked(MyGUI::Widget* sender); From 4344dc6e00431af0be7c2d289bf1217f97628f46 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 5 Aug 2025 19:10:48 +0200 Subject: [PATCH 100/102] Don't init custom data when checking container resolution --- CMakeLists.txt | 2 +- apps/openmw/mwlua/objectbindings.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5991c57c4c..c0d09542f7 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 88) +set(OPENMW_LUA_API_REVISION 89) set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "") diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 3508fdcd44..0a73acfc42 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -645,6 +645,9 @@ namespace MWLua } inventoryT["isResolved"] = [](const InventoryT& inventory) -> bool { const MWWorld::Ptr& ptr = inventory.mObj.ptr(); + // Avoid initializing custom data + if (!ptr.getRefData().getCustomData()) + return false; MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); return store.isResolved(); }; From d68b774c10f0e7fed523029651303cb72c4ebe36 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Tue, 5 Aug 2025 14:12:50 -0700 Subject: [PATCH 101/102] CLEANUP: Don't handle refnums during saving at all --- apps/opencs/model/doc/savingstages.cpp | 13 +++---------- apps/opencs/model/doc/savingstages.hpp | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 85a570d8dd..492807eb93 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -291,8 +291,7 @@ int CSMDoc::WriteCellCollectionStage::setup() return mDocument.getData().getCells().getSize(); } -void CSMDoc::WriteCellCollectionStage::writeReferences( - const std::deque& references, bool interior, unsigned int& newRefNum) +void CSMDoc::WriteCellCollectionStage::writeReferences(const std::deque& references, bool interior) { ESM::ESMWriter& writer = mState.getWriter(); @@ -359,9 +358,6 @@ void CSMDoc::WriteCellCollectionStage::perform(int stage, Messages& messages) CSMWorld::Cell cellRecord = cell.get(); const bool interior = !cellRecord.mId.startsWith("#"); - // count new references and adjust RefNumCount accordingsly - unsigned int newRefNum = cellRecord.mRefNumCounter; - if (references != nullptr) { for (std::deque::const_iterator iter(references->begin()); iter != references->end(); ++iter) @@ -387,9 +383,6 @@ void CSMDoc::WriteCellCollectionStage::perform(int stage, Messages& messages) ESM::RefId::stringRefId(CSMWorld::CellCoordinates(refRecord.getCellIndex()).getId("")) != refRecord.mCell)) ++cellRecord.mRefNumCounter; - - if (refRecord.mRefNum.mIndex >= newRefNum) - newRefNum = refRecord.mRefNum.mIndex + 1; } } @@ -412,9 +405,9 @@ void CSMDoc::WriteCellCollectionStage::perform(int stage, Messages& messages) // write references if (references != nullptr) { - writeReferences(persistentRefs, interior, newRefNum); + writeReferences(persistentRefs, interior); cellRecord.saveTempMarker(writer, static_cast(references->size()) - persistentRefs.size()); - writeReferences(tempRefs, interior, newRefNum); + writeReferences(tempRefs, interior); } writer.endRecord(cellRecord.sRecordId); diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index 5423b8f504..1f86af10a2 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -167,7 +167,7 @@ namespace CSMDoc Document& mDocument; SavingState& mState; - void writeReferences(const std::deque& references, bool interior, unsigned int& newRefNum); + void writeReferences(const std::deque& references, bool interior); public: WriteCellCollectionStage(Document& document, SavingState& state); From dd801a55e2c31b6d11f27dfeb4519801dff1d6e4 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 5 Aug 2025 17:31:36 -0700 Subject: [PATCH 102/102] Bump OPENMW_LUA_API_REVISION version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 57ebeefcfd..201cc600c7 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 87) +set(OPENMW_LUA_API_REVISION 90) set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "")