diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index a4fb7fb0d9..6986b69030 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -39,8 +39,6 @@ #include "utils/profilescombobox.hpp" #include "utils/textinputdialog.hpp" -#include "ui_directorypicker.h" - const char* Launcher::DataFilesPage::mDefaultContentListName = "Default"; namespace @@ -155,6 +153,7 @@ namespace Launcher Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, Config::LauncherSettings& launcherSettings, MainDialog* parent) : QWidget(parent) + , mDirectoryPickerDialog(new QDialog(this)) , mMainDialog(parent) , mCfgMgr(cfg) , mGameSettings(gameSettings) @@ -163,6 +162,7 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C , mReloadCellsThread(&DataFilesPage::reloadCells, this) { ui.setupUi(this); + mDirectoryPicker.setupUi(mDirectoryPickerDialog); setObjectName("DataFilesPage"); mSelector = new ContentSelectorView::ContentSelector(ui.contentSelectorWidget, /*showOMWScripts=*/true); const QString encoding = mGameSettings.value("encoding", { "win1252" }).value; @@ -266,6 +266,7 @@ void Launcher::DataFilesPage::buildView() buildArchiveContextMenu(); buildDataFilesContextMenu(); + buildDirectoryPickerContextMenu(); } void Launcher::DataFilesPage::slotCopySelectedItemsPaths() @@ -298,8 +299,10 @@ void Launcher::DataFilesPage::buildArchiveContextMenu() &DataFilesPage::slotShowArchiveContextMenu); mArchiveContextMenu = new QMenu(ui.archiveListWidget); - mArchiveContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems())); - mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems())); + mArchiveContextMenu->addAction(tr("&Check Selected"), this, + [this]() { setCheckStateForMultiSelectedItems(ui.archiveListWidget, Qt::Checked); }); + mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this, + [this]() { setCheckStateForMultiSelectedItems(ui.archiveListWidget, Qt::Unchecked); }); } void Launcher::DataFilesPage::buildDataFilesContextMenu() @@ -314,6 +317,18 @@ void Launcher::DataFilesPage::buildDataFilesContextMenu() tr("&Open Path in File Explorer"), this, &Launcher::DataFilesPage::slotOpenSelectedItemsPaths); } +void Launcher::DataFilesPage::buildDirectoryPickerContextMenu() +{ + connect(mDirectoryPicker.dirListWidget, &QListWidget::customContextMenuRequested, this, + &DataFilesPage::slotShowDirectoryPickerContextMenu); + + mDirectoryPickerMenu = new QMenu(mDirectoryPicker.dirListWidget); + mDirectoryPickerMenu->addAction(tr("&Check Selected"), this, + [this]() { setCheckStateForMultiSelectedItems(mDirectoryPicker.dirListWidget, Qt::Checked); }); + mDirectoryPickerMenu->addAction(tr("&Uncheck Selected"), this, + [this]() { setCheckStateForMultiSelectedItems(mDirectoryPicker.dirListWidget, Qt::Unchecked); }); +} + bool Launcher::DataFilesPage::loadSettings() { ui.navMeshMaxSizeSpinBox->setValue(getMaxNavMeshDbFileSizeMiB()); @@ -821,28 +836,22 @@ void Launcher::DataFilesPage::addSubdirectories(bool append) return; } - QDialog dialog; - Ui::SelectSubdirs select; - - select.setupUi(&dialog); + mDirectoryPicker.dirListWidget->clear(); for (const auto& dir : subdirs) { if (!ui.directoryListWidget->findItems(dir, Qt::MatchFixedString).isEmpty()) continue; - const auto lastRow = select.dirListWidget->count(); - select.dirListWidget->addItem(dir); - select.dirListWidget->item(lastRow)->setCheckState(Qt::Unchecked); + QListWidgetItem* newDir = new QListWidgetItem(dir, mDirectoryPicker.dirListWidget); + newDir->setCheckState(Qt::Unchecked); } - dialog.show(); - - if (dialog.exec() == QDialog::Rejected) + if (mDirectoryPickerDialog->exec() == QDialog::Rejected) return; - for (int i = 0; i < select.dirListWidget->count(); ++i) + for (int i = 0; i < mDirectoryPicker.dirListWidget->count(); ++i) { - const auto* dir = select.dirListWidget->item(i); + const auto* dir = mDirectoryPicker.dirListWidget->item(i); if (dir->checkState() == Qt::Checked) { ui.directoryListWidget->insertItem(selectedRow, dir->text()); @@ -893,38 +902,35 @@ void Launcher::DataFilesPage::removeDirectory() refreshDataFilesView(); } +void Launcher::DataFilesPage::showContextMenu(QMenu* menu, QListWidget* list, const QPoint& pos) +{ + QPoint globalPos = list->viewport()->mapToGlobal(pos); + menu->exec(globalPos); +} + void Launcher::DataFilesPage::slotShowArchiveContextMenu(const QPoint& pos) { - QPoint globalPos = ui.archiveListWidget->viewport()->mapToGlobal(pos); - mArchiveContextMenu->exec(globalPos); + showContextMenu(mArchiveContextMenu, ui.archiveListWidget, pos); } void Launcher::DataFilesPage::slotShowDataFilesContextMenu(const QPoint& pos) { - QPoint globalPos = ui.directoryListWidget->viewport()->mapToGlobal(pos); - mDataFilesContextMenu->exec(globalPos); + showContextMenu(mDataFilesContextMenu, ui.directoryListWidget, pos); } -void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(bool checked) +void Launcher::DataFilesPage::slotShowDirectoryPickerContextMenu(const QPoint& pos) { - Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked; + showContextMenu(mDirectoryPickerMenu, mDirectoryPicker.dirListWidget, pos); +} - for (QListWidgetItem* selectedItem : ui.archiveListWidget->selectedItems()) +void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(QListWidget* list, Qt::CheckState checkState) +{ + for (QListWidgetItem* selectedItem : list->selectedItems()) { selectedItem->setCheckState(checkState); } } -void Launcher::DataFilesPage::slotUncheckMultiSelectedItems() -{ - setCheckStateForMultiSelectedItems(false); -} - -void Launcher::DataFilesPage::slotCheckMultiSelectedItems() -{ - setCheckStateForMultiSelectedItems(true); -} - void Launcher::DataFilesPage::moveSources(QListWidget* sourceList, int step) { const QList> sortedItems = sortedSelectedItems(sourceList, step > 0); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index ca40bd1319..847cfe5a18 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -2,6 +2,7 @@ #define DATAFILESPAGE_H #include "ui_datafilespage.h" +#include "ui_directorypicker.h" #include @@ -46,8 +47,11 @@ namespace Launcher ContentSelectorView::ContentSelector* mSelector; Ui::DataFilesPage ui; + QDialog* mDirectoryPickerDialog; + Ui::SelectSubdirs mDirectoryPicker; QMenu* mArchiveContextMenu; QMenu* mDataFilesContextMenu; + QMenu* mDirectoryPickerMenu; public: explicit DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, @@ -87,8 +91,7 @@ namespace Launcher void slotShowArchiveContextMenu(const QPoint& pos); void slotShowDataFilesContextMenu(const QPoint& pos); - void slotCheckMultiSelectedItems(); - void slotUncheckMultiSelectedItems(); + void slotShowDirectoryPickerContextMenu(const QPoint& pos); void on_newProfileAction_triggered(); void on_cloneProfileAction_triggered(); @@ -145,7 +148,9 @@ namespace Launcher void buildView(); void buildArchiveContextMenu(); void buildDataFilesContextMenu(); - void setCheckStateForMultiSelectedItems(bool checked); + void buildDirectoryPickerContextMenu(); + void showContextMenu(QMenu* menu, QListWidget* list, const QPoint& pos); + void setCheckStateForMultiSelectedItems(QListWidget* list, Qt::CheckState checkState); void setProfile(int index, bool savePrevious); void setProfile(const QString& previous, const QString& current, bool savePrevious); void removeProfile(const QString& profile); diff --git a/apps/launcher/ui/directorypicker.ui b/apps/launcher/ui/directorypicker.ui index 6350bcd2d3..c32a78b53e 100644 --- a/apps/launcher/ui/directorypicker.ui +++ b/apps/launcher/ui/directorypicker.ui @@ -25,7 +25,14 @@ - + + + QAbstractItemView::ExtendedSelection + + + Qt::CustomContextMenu + + diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 20a27e5bc3..f268ed0e52 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -406,17 +406,16 @@ namespace MWBase virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; - enum RestPermitted + enum RestFlags { - Rest_Allowed = 0, - Rest_OnlyWaiting = 1, + Rest_PlayerIsUnderwater = 1, Rest_PlayerIsInAir = 2, - Rest_PlayerIsUnderwater = 3, - Rest_EnemiesAreNearby = 4 + Rest_EnemiesAreNearby = 4, + Rest_CanSleep = 8, }; /// check if the player is allowed to rest - virtual RestPermitted canRest() const = 0; + virtual int canRest() const = 0; /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 38dbff914a..a398139df4 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -227,7 +227,8 @@ namespace MWGui mGuiMode = mode; const WindowSettingValues settings = getModeSettings(mGuiMode); - setPinButtonVisible(mode == GM_Inventory && !Settings::gui().mControllerMenus); + setPinButtonVisible( + mode != GM_Container && mode != GM_Companion && mode != GM_Barter && !Settings::gui().mControllerMenus); const WindowRectSettingValues& rect = settings.mIsMaximized ? settings.mMaximized : settings.mRegular; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 5b91a46bf8..726e623eb3 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -71,9 +72,6 @@ namespace MWGui price = static_cast(d); } - price = std::max(1, price); - price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); - // Add price for the travelling followers std::set followers; MWWorld::ActionTeleport::getFollowers(player, followers, !interior); @@ -81,6 +79,9 @@ namespace MWGui // Apply followers cost, unlike vanilla the first follower doesn't travel for free price *= 1 + static_cast(followers.size()); + price = std::max(1, price); + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); + const int lineHeight = Settings::gui().mFontSize + 2; MyGUI::Button* toAdd = mDestinationsView->createWidget( @@ -134,12 +135,23 @@ namespace MWGui bool interior = true; const ESM::ExteriorCellLocation cellIndex = ESM::positionToExteriorCellLocation(dest.mPos.pos[0], dest.mPos.pos[1]); + const MWWorld::WorldModel& worldModel = *MWBase::Environment::get().getWorldModel(); if (cellname.empty()) { - MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getExterior(cellIndex); + MWWorld::CellStore& cell = worldModel.getExterior(cellIndex); cellname = MWBase::Environment::get().getWorld()->getCellName(&cell); interior = false; } + else + { + const MWWorld::CellStore* destCell = worldModel.findCell(cellname, false); + if (destCell == nullptr) + { + Log(Debug::Error) << "Failed to add travel destination: unknown cell (" << cellname << ")"; + continue; + } + interior = !destCell->getCell()->isExterior(); + } addDestination(ESM::RefId::stringRefId(cellname), dest.mPos, interior); } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 7edd69f6f4..f33695a982 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -87,15 +87,29 @@ namespace MWGui void WaitDialog::setPtr(const MWWorld::Ptr& ptr) { - setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld()->canRest() == MWBase::World::Rest_Allowed); + const int restFlags = MWBase::Environment::get().getWorld()->canRest(); - if (ptr.isEmpty() && MWBase::Environment::get().getWorld()->canRest() == MWBase::World::Rest_PlayerIsInAir) + const bool underwater = (restFlags & MWBase::World::Rest_PlayerIsUnderwater) != 0; + // Resting in air is allowed if you're using a bed + const bool inAir = ptr.isEmpty() && (restFlags & MWBase::World::Rest_PlayerIsInAir) != 0; + const bool enemiesNearby = (restFlags & MWBase::World::Rest_EnemiesAreNearby) != 0; + const bool solidGround = !underwater && !inAir; + + if (!solidGround || enemiesNearby) { - // Resting in air is not allowed unless you're using a bed - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage1}"); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Rest); + if (!solidGround) + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage1}"); + + if (enemiesNearby) + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + return; } + const bool canSleep = !ptr.isEmpty() || (restFlags & MWBase::World::Rest_CanSleep) != 0; + setCanRest(canSleep); + if (mUntilHealedButton->getVisible()) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton); else @@ -141,20 +155,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->popGuiMode(); } - MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld()->canRest(); - - if (canRest == MWBase::World::Rest_EnemiesAreNearby) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } - else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) - { - // resting underwater not allowed - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage1}"); - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } - onHourSliderChangedPosition(mHourSlider, 0); mHourSlider->setScrollPosition(0); diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index 022aaec262..fc7637afc1 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -274,15 +274,15 @@ namespace MWMechanics CreatureStats& stats = actor.getClass().getCreatureStats(actor); - if (stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Silence).getMagnitude() && !godmode) - return 0; - if (spell->mData.mType == ESM::Spell::ST_Power) return stats.getSpells().canUsePower(spell) ? 100 : 0; if (godmode) return 100; + if (stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Silence).getMagnitude()) + return 0; + if (spell->mData.mType != ESM::Spell::ST_Spell) return 100; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index e236db4633..5e7c70788d 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -656,20 +656,18 @@ namespace MWPhysics auto ptr = physicActor->getPtr(); if (!ptr.getClass().isMobile(ptr)) continue; - float waterlevel = -std::numeric_limits::max(); - const MWWorld::CellStore* cell = ptr.getCell(); - if (cell->getCell()->hasWater()) - waterlevel = cell->getWaterLevel(); + const MWWorld::CellStore& cell = *ptr.getCell(); const auto& stats = ptr.getClass().getCreatureStats(ptr); const MWMechanics::MagicEffects& effects = stats.getMagicEffects(); + float waterlevel = -std::numeric_limits::max(); bool waterCollision = false; - if (cell->getCell()->hasWater() && effects.getOrDefault(ESM::MagicEffect::WaterWalking).getMagnitude()) + if (cell.getCell()->hasWater()) { - if (physicActor->getCollisionMode() - || !world->isUnderwater(ptr.getCell(), ptr.getRefData().getPosition().asVec3())) - waterCollision = true; + waterlevel = cell.getWaterLevel(); + if (physicActor->getCollisionMode()) + waterCollision = effects.getOrDefault(ESM::MagicEffect::WaterWalking).getMagnitude(); } physicActor->setCanWaterWalk(waterCollision); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c642d14b16..f608b8c781 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2333,34 +2333,35 @@ namespace MWWorld Log(Debug::Warning) << "Player agent bounds are not supported by navigator: " << agentBounds; } - World::RestPermitted World::canRest() const + int World::canRest() const { + int result = 0; + CellStore* currentCell = mWorldScene->getCurrentCell(); Ptr player = mPlayer->getPlayer(); - RefData& refdata = player.getRefData(); - osg::Vec3f playerPos(refdata.getPosition().asVec3()); const MWPhysics::Actor* actor = mPhysics->getActor(player); if (!actor) throw std::runtime_error("can't find player"); - if (mPlayer->enemiesNearby()) - return Rest_EnemiesAreNearby; - + const osg::Vec3f playerPos(player.getRefData().getPosition().asVec3()); if (isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) - return Rest_PlayerIsUnderwater; + result |= Rest_PlayerIsUnderwater; float fallHeight = player.getClass().getCreatureStats(player).getFallHeight(); float epsilon = 1e-4; if ((actor->getCollisionMode() && (!mPhysics->isOnSolidGround(player) || fallHeight >= epsilon)) || isFlying(player)) - return Rest_PlayerIsInAir; + result |= Rest_PlayerIsInAir; - if (currentCell->getCell()->noSleep() || player.getClass().getNpcStats(player).isWerewolf()) - return Rest_OnlyWaiting; + if (mPlayer->enemiesNearby()) + result |= Rest_EnemiesAreNearby; - return Rest_Allowed; + if (!currentCell->getCell()->noSleep() && !player.getClass().getNpcStats(player).isWerewolf()) + result |= Rest_CanSleep; + + return result; } MWRender::Animation* World::getAnimation(const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 983682a98f..b1286d5532 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -505,7 +505,7 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; - RestPermitted canRest() const override; + int canRest() const override; ///< check if the player is allowed to rest void rest(double hours) override;