diff --git a/AUTHORS.md b/AUTHORS.md index f6a62c4ce..5b848fab4 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -29,6 +29,7 @@ Programmers Ardekantur Armin Preiml Artem Kotsynyak (greye) + Artem Nykolenko (anikm21) artemutin Arthur Moore (EmperorArthur) Assumeru diff --git a/CHANGELOG.md b/CHANGELOG.md index a7b346414..29e310b58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,24 +2,30 @@ ------ Bug #1515: Opening console masks dialogue, inventory menu + Bug #2395: Duplicated plugins in the launcher when multiple data directories provide the same plugin Bug #2969: Scripted items can stack Bug #2987: Editor: some chance and AI data fields can overflow Bug #3006: 'else if' operator breaks script compilation Bug #3109: SetPos/Position handles actors differently Bug #3282: Unintended behaviour when assigning F3 and Windows keys + Bug #3550: Companion from mod attacks the air after combat has ended Bug #3623: Display scaling breaks mouse recognition Bug #3725: Using script function in a non-conditional expression breaks script compilation Bug #3733: Normal maps are inverted on mirrored UVs Bug #3765: DisableTeleporting makes Mark/Recall/Intervention effects undetectable Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled + Bug #4202: Open .omwaddon files without needing toopen openmw-cs first Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect + Bug #4276: Resizing character window differs from vanilla Bug #4329: Removed birthsign abilities are restored after reloading the save Bug #4341: Error message about missing GDB is too vague Bug #4383: Bow model obscures crosshair when arrow is drawn Bug #4384: Resist Normal Weapons only checks ammunition for ranged weapons Bug #4411: Reloading a saved game while falling prevents damage in some cases Bug #4540: Rain delay when exiting water + Bug #4600: Crash when no sound output is available or --no-sound is used. + Bug #4639: Black screen after completing first mages guild mission + training Bug #4701: PrisonMarker record is not hardcoded like other markers Bug #4703: Editor: it's possible to preview levelled list records Bug #4705: Editor: unable to open exterior cell views from Instances table @@ -99,6 +105,18 @@ Bug #5038: Enchanting success chance calculations are blatantly wrong Bug #5047: # in cell names sets color Bug #5050: Invalid spell effects are not handled gracefully + Bug #5056: Calling Cast function on player doesn't equip the spell but casts it + Bug #5060: Magic effect visuals stop when death animation begins instead of when it ends + Bug #5063: Shape named "Tri Shadow" in creature mesh is visible if it isn't hidden + Bug #5069: Blocking creatures' attacks doesn't degrade shields + Bug #5074: Paralyzed actors greet the player + Bug #5075: Enchanting cast style can be changed if there's no object + Bug #5082: Scrolling with controller in GUI mode is broken + Bug #5092: NPCs with enchanted weapons play sound when out of charges + Bug #5093: Hand to hand sound plays on knocked out enemies + Bug #5099: Non-swimming enemies will enter water if player is water walking + Bug #5105: NPCs start combat with werewolves from any distance + Bug #5110: ModRegion with a redundant numerical argument breaks script execution Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls @@ -126,14 +144,18 @@ Feature #4968: Scalable UI widget skins Feature #4994: Persistent pinnable windows hiding Feature #5000: Compressed BSA format support + Feature #5005: Editor: Instance window via Scene window Feature #5010: Native graphics herbalism support Feature #5031: Make GetWeaponType function return different values for tools Feature #5033: Magic armor mitigation for creatures Feature #5034: Make enchanting window stay open after a failed attempt Feature #5036: Allow scripted faction leaving + Feature #5046: Gamepad thumbstick cursor speed Feature #5051: Provide a separate textures for scrollbars + Feature #5094: Unix like console hotkeys Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption + Task #4789: Optimize cell transitions Task #4721: Add NMake support to the Windows prebuild script 0.45.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index cc2759f50..0e7647aa6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,6 +222,9 @@ endif() # Required for building the FFmpeg headers add_definitions(-D__STDC_CONSTANT_MACROS) +# Reqiuired for unity build +add_definitions(-DMYGUI_DONT_REPLACE_NULLPTR) + # TinyXML option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) if (USE_SYSTEM_TINYXML) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 055426f30..8554b620b 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -109,15 +109,14 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) { QStringList paths = mGameSettings.getDataDirs(); - foreach(const QString &path, paths) - mSelector->addFiles(path); - mDataLocal = mGameSettings.getDataLocal(); if (!mDataLocal.isEmpty()) - mSelector->addFiles(mDataLocal); + paths.insert(0, mDataLocal); + + foreach(const QString &path, paths) + mSelector->addFiles(path); - paths.insert(0, mDataLocal); PathIterator pathIterator(paths); mSelector->setProfileContent(filesInProfile(contentModelName, pathIterator)); diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 56cd08680..c991e79f4 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -44,6 +44,7 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings: connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int))); connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool))); connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int))); + connect(framerateLimitCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotFramerateLimitToggled(bool))); } @@ -121,6 +122,13 @@ bool Launcher::GraphicsPage::loadSettings() customHeightSpinBox->setValue(height); } + float fpsLimit = mEngineSettings.getFloat("framerate limit", "Video"); + if (fpsLimit != 0) + { + framerateLimitCheckBox->setCheckState(Qt::Checked); + framerateLimitSpinBox->setValue(fpsLimit); + } + return true; } @@ -166,6 +174,17 @@ void Launcher::GraphicsPage::saveSettings() int cScreen = screenComboBox->currentIndex(); if (cScreen != mEngineSettings.getInt("screen", "Video")) mEngineSettings.setInt("screen", "Video", cScreen); + + if (framerateLimitCheckBox->checkState()) + { + float cFpsLimit = framerateLimitSpinBox->value(); + if (cFpsLimit != mEngineSettings.getFloat("framerate limit", "Video")) + mEngineSettings.setFloat("framerate limit", "Video", cFpsLimit); + } + else if (mEngineSettings.getFloat("framerate limit", "Video") != 0) + { + mEngineSettings.setFloat("framerate limit", "Video", 0); + } } QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) @@ -266,3 +285,8 @@ void Launcher::GraphicsPage::slotStandardToggled(bool checked) customHeightSpinBox->setEnabled(true); } } + +void Launcher::GraphicsPage::slotFramerateLimitToggled(bool checked) +{ + framerateLimitSpinBox->setEnabled(checked); +} diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index 9d943d5e2..05915e680 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -31,6 +31,7 @@ namespace Launcher private slots: void slotFullScreenChanged(int state); void slotStandardToggled(bool checked); + void slotFramerateLimitToggled(bool checked); private: Files::ConfigurationManager &mCfgMgr; diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 7b5c3f7ab..0ce031548 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -27,8 +27,11 @@ CS::Editor::Editor (int argc, char **argv) std::pair > config = readConfig(); mViewManager = new CSVDoc::ViewManager(mDocumentManager); - - setupDataFiles (config.first); + if (argc > 1) + { + mFileToLoad = argv[1]; + mDataDirs = config.first; + } NifOsg::Loader::setShowMarkers(true); @@ -79,15 +82,6 @@ CS::Editor::~Editor () remove(mPid.string().c_str())); // ignore any error } -void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs) -{ - for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) - { - QString path = QString::fromUtf8 (iter->string().c_str()); - mFileDialog.addFiles(path); - } -} - std::pair > CS::Editor::readConfig(bool quiet) { boost::program_options::variables_map variables; @@ -114,9 +108,9 @@ std::pair > CS::Editor::readConfi Fallback::Map::init(variables["fallback"].as().mMap); - const std::string encoding = variables["encoding"].as().toStdString(); - mDocumentManager.setEncoding (ToUTF8::calculateEncoding (encoding)); - mFileDialog.setEncoding (QString::fromUtf8(encoding.c_str())); + mEncodingName = variables["encoding"].as().toStdString(); + mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName)); + mFileDialog.setEncoding (QString::fromUtf8(mEncodingName.c_str())); mDocumentManager.setResourceDir (mResources = variables["resources"].as().toStdString()); @@ -160,7 +154,7 @@ std::pair > CS::Editor::readConfi dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); //iterate the data directories and add them to the file dialog for loading - for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) + for (Files::PathContainer::const_reverse_iterator iter = dataDirs.rbegin(); iter != dataDirs.rend(); ++iter) { QString path = QString::fromUtf8 (iter->string().c_str()); mFileDialog.addFiles(path); @@ -199,8 +193,7 @@ void CS::Editor::createAddon() mStartup.hide(); mFileDialog.clearFiles(); - std::pair > config = readConfig(/*quiet*/true); - setupDataFiles (config.first); + readConfig(/*quiet*/true); mFileDialog.showDialog (CSVDoc::ContentAction_New); } @@ -224,18 +217,24 @@ void CS::Editor::loadDocument() mStartup.hide(); mFileDialog.clearFiles(); - std::pair > config = readConfig(/*quiet*/true); - setupDataFiles (config.first); + readConfig(/*quiet*/true); mFileDialog.showDialog (CSVDoc::ContentAction_Edit); } -void CS::Editor::openFiles (const boost::filesystem::path &savePath) +void CS::Editor::openFiles (const boost::filesystem::path &savePath, const std::vector &discoveredFiles) { std::vector files; - foreach (const QString &path, mFileDialog.selectedFilePaths()) - files.push_back(path.toUtf8().constData()); + if(discoveredFiles.empty()) + { + foreach(const QString &path, mFileDialog.selectedFilePaths()) + files.push_back(path.toUtf8().constData()); + } + else + { + files = discoveredFiles; + } mDocumentManager.addDocument (files, savePath, false); @@ -361,9 +360,53 @@ int CS::Editor::run() Misc::Rng::init(); - mStartup.show(); + QApplication::setQuitOnLastWindowClosed(true); - QApplication::setQuitOnLastWindowClosed (true); + if (mFileToLoad.empty()) + { + mStartup.show(); + } + else + { + ESM::ESMReader fileReader; + ToUTF8::Utf8Encoder encoder = ToUTF8::calculateEncoding(mEncodingName); + fileReader.setEncoder(&encoder); + fileReader.open(mFileToLoad.string()); + + std::vector discoveredFiles; + + for (std::vector::const_iterator itemIter = fileReader.getGameFiles().begin(); + itemIter != fileReader.getGameFiles().end(); ++itemIter) + { + for (Files::PathContainer::const_iterator pathIter = mDataDirs.begin(); + pathIter != mDataDirs.end(); ++pathIter) + { + const boost::filesystem::path masterPath = *pathIter / itemIter->name; + if (boost::filesystem::exists(masterPath)) + { + discoveredFiles.push_back(masterPath); + break; + } + } + } + discoveredFiles.push_back(mFileToLoad); + + QString extension = QString::fromStdString(mFileToLoad.extension().string()).toLower(); + if (extension == ".esm") + { + mFileToLoad.replace_extension(".omwgame"); + mDocumentManager.addDocument(discoveredFiles, mFileToLoad, false); + } + else if (extension == ".esp") + { + mFileToLoad.replace_extension(".omwaddon"); + mDocumentManager.addDocument(discoveredFiles, mFileToLoad, false); + } + else + { + openFiles(mFileToLoad, discoveredFiles); + } + } return QApplication::exec(); } diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 2c2e88aef..1c9342761 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -54,8 +54,9 @@ namespace CS bool mFsStrict; CSVTools::Merge mMerge; CSVDoc::ViewManager* mViewManager; - - void setupDataFiles (const Files::PathContainer& dataDirs); + boost::filesystem::path mFileToLoad; + Files::PathContainer mDataDirs; + std::string mEncodingName; std::pair > readConfig(bool quiet=false); ///< \return data paths @@ -83,7 +84,7 @@ namespace CS void cancelFileDialog(); void loadDocument(); - void openFiles (const boost::filesystem::path &path); + void openFiles (const boost::filesystem::path &path, const std::vector &discoveredFiles = std::vector()); void createNewFile (const boost::filesystem::path& path); void createNewGame (const boost::filesystem::path& file); diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 9bc0e0877..bfe907c19 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "intsetting.hpp" #include "doublesetting.hpp" @@ -247,6 +248,9 @@ void CSMPrefs::State::declare() addValues (landeditOutsideVisibleCell); declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50). setMin (1); + declareBool ("open-list-view", "Open displays list view", false). + setTooltip ("When opening a reference from the scene view, it will open the" + " instance list view instead of the individual instance record view."); declareCategory ("Key Bindings"); @@ -331,6 +335,7 @@ void CSMPrefs::State::declare() declareShortcut ("scene-navi-primary", "Camera Rotation From Mouse Movement", QKeySequence(Qt::LeftButton)); declareShortcut ("scene-navi-secondary", "Camera Translation From Mouse Movement", QKeySequence(Qt::ControlModifier | (int)Qt::LeftButton)); + declareShortcut ("scene-open-primary", "Primary Open", QKeySequence(Qt::ShiftModifier | (int)Qt::LeftButton)); declareShortcut ("scene-edit-primary", "Primary Edit", QKeySequence(Qt::RightButton)); declareShortcut ("scene-edit-secondary", "Secondary Edit", QKeySequence(Qt::ControlModifier | (int)Qt::RightButton)); @@ -411,7 +416,9 @@ CSMPrefs::DoubleSetting& CSMPrefs::State::declareDouble (const std::string& key, if (mCurrentCategory==mCategories.end()) throw std::logic_error ("no category for setting"); - setDefault(key, std::to_string(default_)); + std::ostringstream stream; + stream << default_; + setDefault(key, stream.str()); default_ = mSettings.getFloat (key, mCurrentCategory->second.getKey()); diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 249b43252..5dda78919 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -422,7 +422,7 @@ namespace static const char *sApparatusTypes[] = { - "Mortar & Pestle", "Albemic", "Calcinator", "Retort", 0 + "Mortar & Pestle", "Alembic", "Calcinator", "Retort", 0 }; static const char *sArmorTypes[] = diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 42dbe51ac..343495518 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -22,6 +22,7 @@ #include "../../model/world/idtable.hpp" #include "../world/subviews.hpp" +#include "../world/scenesubview.hpp" #include "../world/tablesubview.hpp" #include "../tools/subviews.hpp" @@ -626,6 +627,20 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin connect (view, SIGNAL (updateSubViewIndices (SubView *)), this, SLOT (updateSubViewIndices (SubView *))); + CSVWorld::TableSubView* tableView = dynamic_cast(view); + if (tableView) + { + connect (this, SIGNAL (requestFocus (const std::string&)), + tableView, SLOT (requestFocus (const std::string&))); + } + + CSVWorld::SceneSubView* sceneView = dynamic_cast(view); + if (sceneView) + { + connect(sceneView, SIGNAL(requestFocus(const std::string&)), + this, SLOT(onRequestFocus(const std::string&))); + } + view->show(); if (!hint.empty()) @@ -1065,3 +1080,16 @@ void CSVDoc::View::createScrollArea() mScroll->setWidget(&mSubViewWindow); setCentralWidget(mScroll); } + +void CSVDoc::View::onRequestFocus (const std::string& id) +{ + if(CSMPrefs::get()["3D Scene Editing"]["open-list-view"].isTrue()) + { + addReferencesSubView(); + emit requestFocus(id); + } + else + { + addSubView(CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Reference, id)); + } +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index c4046a7a1..52057ab37 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -140,6 +140,8 @@ namespace CSVDoc void mergeDocument (CSMDoc::Document *document); + void requestFocus (const std::string& id); + public slots: void addSubView (const CSMWorld::UniversalId& id, const std::string& hint = ""); @@ -262,6 +264,8 @@ namespace CSVDoc void moveScrollBarToEnd(int min, int max); void merge(); + + void onRequestFocus (const std::string& id); }; } diff --git a/apps/opencs/view/render/editmode.cpp b/apps/opencs/view/render/editmode.cpp index 5f0852c90..03451bc1b 100644 --- a/apps/opencs/view/render/editmode.cpp +++ b/apps/opencs/view/render/editmode.cpp @@ -29,6 +29,8 @@ void CSVRender::EditMode::setEditLock (bool locked) } +void CSVRender::EditMode::primaryOpenPressed (const WorldspaceHitResult& hit) {} + void CSVRender::EditMode::primaryEditPressed (const WorldspaceHitResult& hit) {} void CSVRender::EditMode::secondaryEditPressed (const WorldspaceHitResult& hit) {} diff --git a/apps/opencs/view/render/editmode.hpp b/apps/opencs/view/render/editmode.hpp index f9b7027f9..9f3b28957 100644 --- a/apps/opencs/view/render/editmode.hpp +++ b/apps/opencs/view/render/editmode.hpp @@ -39,6 +39,9 @@ namespace CSVRender /// Default-implementation: Ignored. virtual void setEditLock (bool locked); + /// Default-implementation: Ignored. + virtual void primaryOpenPressed (const WorldspaceHitResult& hit); + /// Default-implementation: Ignored. virtual void primaryEditPressed (const WorldspaceHitResult& hit); diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 1cf8a5698..0cf58038d 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -94,6 +94,8 @@ CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidg parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None), mDragAxis (-1), mLocked (false), mUnitScaleDist(1) { + connect(this, SIGNAL(requestFocus(const std::string&)), + worldspaceWidget, SIGNAL(requestFocus(const std::string&))); } void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) @@ -174,6 +176,18 @@ void CSVRender::InstanceMode::primaryEditPressed (const WorldspaceHitResult& hit primarySelectPressed (hit); } +void CSVRender::InstanceMode::primaryOpenPressed (const WorldspaceHitResult& hit) +{ + if(hit.tag) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (hit.tag.get())) + { + const std::string refId = objectTag->mObject->getReferenceId(); + emit requestFocus(refId); + } + } +} + void CSVRender::InstanceMode::secondaryEditPressed (const WorldspaceHitResult& hit) { if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 933cae529..6ddaa254f 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -55,6 +55,8 @@ namespace CSVRender virtual void setEditLock (bool locked); + virtual void primaryOpenPressed (const WorldspaceHitResult& hit); + virtual void primaryEditPressed (const WorldspaceHitResult& hit); virtual void secondaryEditPressed (const WorldspaceHitResult& hit); @@ -83,6 +85,10 @@ namespace CSVRender virtual int getSubMode() const; + signals: + + void requestFocus (const std::string& id); + private slots: void subModeChanged (const std::string& id); diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index a9cce0200..8863ad235 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -63,6 +63,10 @@ namespace CSVRender } } + void PathgridMode::primaryOpenPressed(const WorldspaceHitResult& hitResult) + { + } + void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hitResult) { if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue() && diff --git a/apps/opencs/view/render/pathgridmode.hpp b/apps/opencs/view/render/pathgridmode.hpp index e34208f8c..a012a67e4 100644 --- a/apps/opencs/view/render/pathgridmode.hpp +++ b/apps/opencs/view/render/pathgridmode.hpp @@ -21,6 +21,8 @@ namespace CSVRender virtual void deactivate(CSVWidget::SceneToolbar* toolbar); + virtual void primaryOpenPressed(const WorldspaceHitResult& hit); + virtual void primaryEditPressed(const WorldspaceHitResult& hit); virtual void secondaryEditPressed(const WorldspaceHitResult& hit); diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index 274e64742..4205188e4 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -77,6 +77,10 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar) EditMode::deactivate(toolbar); } +void CSVRender::TerrainTextureMode::primaryOpenPressed(const WorldspaceHitResult& hit) // Apply changes here +{ +} + void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult& hit) // Apply changes here { CSMDoc::Document& document = getWorldspaceWidget().getDocument(); diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp index 5184f0f73..10ea842c9 100644 --- a/apps/opencs/view/render/terraintexturemode.hpp +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -35,6 +35,8 @@ namespace CSVRender /// \brief Editmode for terrain texture grid TerrainTextureMode(WorldspaceWidget*, QWidget* parent = nullptr); + void primaryOpenPressed (const WorldspaceHitResult& hit); + /// \brief Create single command for one-click texture editing void primaryEditPressed (const WorldspaceHitResult& hit); diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 7e405266e..1eca61e73 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -101,6 +101,9 @@ CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidg // Shortcuts CSMPrefs::Shortcut* primaryEditShortcut = new CSMPrefs::Shortcut("scene-edit-primary", "scene-speed-modifier", CSMPrefs::Shortcut::SM_Detach, this); + CSMPrefs::Shortcut* primaryOpenShortcut = new CSMPrefs::Shortcut("scene-open-primary", this); + + connect(primaryOpenShortcut, SIGNAL(activated(bool)), this, SLOT(primaryOpen(bool))); connect(primaryEditShortcut, SIGNAL(activated(bool)), this, SLOT(primaryEdit(bool))); connect(primaryEditShortcut, SIGNAL(secondary(bool)), this, SLOT(speedMode(bool))); @@ -696,6 +699,8 @@ void CSVRender::WorldspaceWidget::handleInteractionPress (const WorldspaceHitRes editMode.primarySelectPressed (hit); else if (type == InteractionType_SecondarySelect) editMode.secondarySelectPressed (hit); + else if (type == InteractionType_PrimaryOpen) + editMode.primaryOpenPressed (hit); } CSVRender::EditMode *CSVRender::WorldspaceWidget::getEditMode() @@ -703,6 +708,11 @@ CSVRender::EditMode *CSVRender::WorldspaceWidget::getEditMode() return dynamic_cast (mEditMode->getCurrent()); } +void CSVRender::WorldspaceWidget::primaryOpen(bool activate) +{ + handleInteraction(InteractionType_PrimaryOpen, activate); +} + void CSVRender::WorldspaceWidget::primaryEdit(bool activate) { handleInteraction(InteractionType_PrimaryEdit, activate); diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 9160ca47e..06c182b0c 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -91,6 +91,7 @@ namespace CSVRender InteractionType_PrimarySelect, InteractionType_SecondaryEdit, InteractionType_SecondarySelect, + InteractionType_PrimaryOpen, InteractionType_None }; @@ -263,6 +264,8 @@ namespace CSVRender void showToolTip(); + void primaryOpen(bool activate); + void primaryEdit(bool activate); void secondaryEdit(bool activate); @@ -283,6 +286,8 @@ namespace CSVRender void dataDropped(const std::vector& data); + void requestFocus (const std::string& id); + friend class MouseState; }; } diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 4b129d32a..44542c529 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -83,6 +83,9 @@ void CSVWorld::SceneSubView::makeConnections (CSVRender::UnpagedWorldspaceWidget connect(widget, SIGNAL(cellChanged(const CSMWorld::UniversalId&)), this, SLOT(cellSelectionChanged(const CSMWorld::UniversalId&))); + + connect(widget, SIGNAL(requestFocus (const std::string&)), + this, SIGNAL(requestFocus (const std::string&))); } void CSVWorld::SceneSubView::makeConnections (CSVRender::PagedWorldspaceWidget* widget) @@ -94,6 +97,9 @@ void CSVWorld::SceneSubView::makeConnections (CSVRender::PagedWorldspaceWidget* connect (widget, SIGNAL (cellSelectionChanged (const CSMWorld::CellSelection&)), this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&))); + + connect(widget, SIGNAL(requestFocus (const std::string&)), + this, SIGNAL(requestFocus (const std::string&))); } CSVWidget::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type) diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index 0f18e8c30..85f7d0925 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -82,6 +82,10 @@ namespace CSVWorld void cellSelectionChanged (const CSMWorld::UniversalId& id); void handleDrop(const std::vector& data); + + signals: + + void requestFocus (const std::string& id); }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index eb7b8e334..11c2be5fc 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -737,7 +737,12 @@ void CSVWorld::Table::requestFocus (const std::string& id) QModelIndex index = mProxyModel->getModelIndex (id, 0); if (index.isValid()) - scrollTo (index, QAbstractItemView::PositionAtTop); + { + // This will scroll to the row. + selectRow (index.row()); + // This will actually select it. + selectionModel()->select (index, QItemSelectionModel::Select | QItemSelectionModel::Rows); + } } void CSVWorld::Table::recordFilterChanged (std::shared_ptr filter) diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 12e29995d..b0bae7fdf 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -165,3 +165,8 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) } return false; } + +void CSVWorld::TableSubView::requestFocus (const std::string& id) +{ + mTable->requestFocus(id); +} diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 7d143d927..1adf862d5 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -60,6 +60,10 @@ namespace CSVWorld void cloneRequest (const CSMWorld::UniversalId& toClone); void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, Qt::DropAction action); + + public slots: + + void requestFocus (const std::string& id); }; } diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index a8406903c..fa94ba039 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -375,6 +375,9 @@ namespace MWBase virtual const Translation::Storage& getTranslationDataStorage() const = 0; + /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. + virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; + virtual void loadUserFonts() = 0; virtual Loading::Listener* getLoadingScreen() = 0; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 588fb4c57..b1c817e6a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1365,7 +1365,7 @@ namespace MWClass MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); // Race weight should not affect 1st-person meshes, otherwise it will change hand proportions and can break aiming. - if (ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson()) + if (ptr == MWMechanics::getPlayer() && ptr.isInCell() && MWBase::Environment::get().getWorld()->isFirstPerson()) { if (ref->mBase->isMale()) scale *= race->mData.mHeight.mMale; diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index c1bdd023f..ad66735bf 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -169,7 +168,7 @@ namespace MWGui update(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mNameEdit); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); } void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 8955606d2..6f6a621ad 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -72,7 +71,7 @@ namespace MWGui WindowModal::onOpen(); updateBirths(); updateSpells(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mBirthList); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mBirthList); // Show the current birthsign by default const std::string &signId = diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index e4cf254e2..86089051d 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -101,7 +101,7 @@ namespace MWGui setTakeButtonShow(showTakeButton); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); } void BookWindow::setTakeButtonShow(bool show) @@ -161,9 +161,9 @@ namespace MWGui mPrevPageButton->setVisible(prevPageVisible); if (focus == mNextPageButton && !nextPageVisible && prevPageVisible) - MyGUI::InputManager::getInstance().setKeyFocusWidget(mPrevPageButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mPrevPageButton); else if (focus == mPrevPageButton && !prevPageVisible && nextPageVisible) - MyGUI::InputManager::getInstance().setKeyFocusWidget(mNextPageButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNextPageButton); if (mPages.empty()) return; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 94c493be7..a92ad934c 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -136,7 +135,7 @@ namespace MWGui WindowModal::onOpen (); updateClasses(); updateStats(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mClassList); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mClassList); // Show the current class by default MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -437,7 +436,7 @@ namespace MWGui getWidget(mEditName, "EditName"); // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mEditName); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mEditName); MyGUI::Button* descriptionButton; getWidget(descriptionButton, "DescriptionButton"); @@ -903,7 +902,7 @@ namespace MWGui okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sInputMenu1", "")); // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } DescriptionDialog::~DescriptionDialog() diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index c5a50c2e6..65b079d85 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -34,7 +33,7 @@ namespace MWGui mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height + 24); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mOkButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton); center(); } diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 8174b1f41..df3c2cb49 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -153,7 +153,7 @@ namespace MWGui { // Give keyboard focus to the combo box whenever the console is // turned on and place it over other widgets - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCommandLine); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); MyGUI::LayerManager::getInstance().upLayerItem(mMainWidget); } @@ -238,11 +238,48 @@ namespace MWGui resetReference(); } + bool isWhitespace(char c) + { + return c == ' ' || c == '\t'; + } + void Console::keyPress(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char _char) { - if( key == MyGUI::KeyCode::Tab) + if(MyGUI::InputManager::getInstance().isControlPressed()) + { + if(key == MyGUI::KeyCode::W) + { + const auto& caption = mCommandLine->getCaption(); + if(caption.empty()) + return; + size_t max = mCommandLine->getTextCursor(); + while(max > 0 && (isWhitespace(caption[max - 1]) || caption[max - 1] == '>')) + max--; + while(max > 0 && !isWhitespace(caption[max - 1]) && caption[max - 1] != '>') + max--; + size_t length = mCommandLine->getTextCursor() - max; + if(length > 0) + { + std::string text = caption; + text.erase(max, length); + mCommandLine->setCaption(text); + mCommandLine->setTextCursor(max); + } + } + else if(key == MyGUI::KeyCode::U) + { + if(mCommandLine->getTextCursor() > 0) + { + std::string text = mCommandLine->getCaption(); + text.erase(0, mCommandLine->getTextCursor()); + mCommandLine->setCaption(text); + mCommandLine->setTextCursor(0); + } + } + } + else if(key == MyGUI::KeyCode::Tab) { std::vector matches; listNames(); @@ -474,7 +511,7 @@ namespace MWGui mPtr = object; } // User clicked on an object. Restore focus to the console command line. - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCommandLine); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } else { diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 7c4f20004..8c7084e8d 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -254,7 +254,7 @@ namespace MWGui mItemView->setModel (mSortModel); mItemView->resetScrollBars(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); } @@ -298,7 +298,7 @@ namespace MWGui if(mDragAndDrop != nullptr && mDragAndDrop->mIsOnDragAndDrop) return; - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); /* Start of tes3mp addition @@ -378,7 +378,7 @@ namespace MWGui { if(mDragAndDrop == nullptr || !mDragAndDrop->mIsOnDragAndDrop) { - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); onTakeAllButtonClicked(mTakeButton); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 30ef50759..baf3a43ab 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include @@ -48,7 +47,7 @@ namespace MWGui mMainWidget->getHeight()); // by default, the text edit field has the focus of the keyboard - MyGUI::InputManager::getInstance().setKeyFocusWidget(mItemEdit); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mItemEdit); mSlider->setScrollPosition(maxCount-1); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 4c9e6b230..6b400c172 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -381,7 +380,7 @@ namespace MWGui { onTopicActivated(topic); if (mGoodbyeButton->getEnabled()) - MyGUI::InputManager::getInstance().setKeyFocusWidget(mGoodbyeButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton); } else if (topic == sPersuasion) mPersuasionDialog.setVisible(true); @@ -444,7 +443,7 @@ namespace MWGui return; } - MyGUI::InputManager::getInstance().setKeyFocusWidget(mGoodbyeButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton); setTitle(mPtr.getClass().getName(mPtr)); @@ -623,7 +622,7 @@ namespace MWGui bool goodbyeWasEnabled = mGoodbyeButton->getEnabled(); mGoodbyeButton->setEnabled(goodbyeEnabled); if (goodbyeEnabled && !goodbyeWasEnabled) - MyGUI::InputManager::getInstance().setKeyFocusWidget(mGoodbyeButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton); bool topicsEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() && !mGoodbye; mTopicsList->setEnabled(topicsEnabled); diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 41f82bd2f..d0d2118c6 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -68,7 +67,7 @@ namespace MWGui void EnchantingDialog::onOpen() { center(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mName); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mName); } void EnchantingDialog::setSoulGem(const MWWorld::Ptr &gem) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 34c0fa8e1..43e7edf1e 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -254,7 +254,7 @@ namespace } updateShowingPages(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(getWidget(CloseBTN)); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(getWidget(CloseBTN)); } void onClose() @@ -377,9 +377,9 @@ namespace prevPageBtn->setVisible(prevPageVisible); if (focus == nextPageBtn && !nextPageVisible && prevPageVisible) - MyGUI::InputManager::getInstance().setKeyFocusWidget(prevPageBtn); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(prevPageBtn); else if (focus == prevPageBtn && !prevPageVisible && nextPageVisible) - MyGUI::InputManager::getInstance().setKeyFocusWidget(nextPageBtn); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nextPageBtn); setVisible (PageOneNum, relPages > 0); setVisible (PageTwoNum, relPages > 1); diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index 79b9e8457..7355dc1f4 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -83,7 +83,7 @@ void KeyboardNavigation::restoreFocus(int mode) { MyGUI::Widget* w = found->second; if (w && w->getVisible() && w->getEnabled()) - MyGUI::InputManager::getInstance().setKeyFocusWidget(found->second); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(found->second); } } @@ -130,7 +130,7 @@ void KeyboardNavigation::onFrame() // workaround incorrect key focus resets (fix in MyGUI TBD) if (!shouldAcceptKeyFocus(focus) && shouldAcceptKeyFocus(mCurrentFocus) && (!mModalWindow || isRootParent(mCurrentFocus, mModalWindow))) { - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCurrentFocus); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCurrentFocus); focus = mCurrentFocus; } @@ -154,12 +154,12 @@ void KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *d MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); if (!focus || !shouldAcceptKeyFocus(focus)) { - MyGUI::InputManager::getInstance().setKeyFocusWidget(defaultFocus); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(defaultFocus); } else { if (!isRootParent(focus, window)) - MyGUI::InputManager::getInstance().setKeyFocusWidget(defaultFocus); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(defaultFocus); } } @@ -276,7 +276,7 @@ bool KeyboardNavigation::switchFocus(int direction, bool wrap) else if (direction == D_Up && (vertdiff >= 0 || !isVertical)) return false; - MyGUI::InputManager::getInstance().setKeyFocusWidget(keyFocusList[index]); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[index]); return true; } @@ -291,7 +291,7 @@ bool KeyboardNavigation::selectFirstWidget() if (!keyFocusList.empty()) { - MyGUI::InputManager::getInstance().setKeyFocusWidget(keyFocusList[0]); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[0]); return true; } return false; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 4b2612604..b2df52c5a 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -68,12 +67,12 @@ namespace MWGui if (isMainMenu) { if (mButtons["loadgame"]->getVisible()) - MyGUI::InputManager::getInstance().setKeyFocusWidget(mButtons["loadgame"]); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mButtons["loadgame"]); else - MyGUI::InputManager::getInstance().setKeyFocusWidget(mButtons["newgame"]); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mButtons["newgame"]); } else - MyGUI::InputManager::getInstance().setKeyFocusWidget(mButtons["return"]); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mButtons["return"]); } Layout::setVisible (visible); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index d0bbe7563..da25f7d27 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1196,7 +1196,7 @@ namespace MWGui { WindowModal::onOpen(); center(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } void EditNoteDialog::onCancelButtonClicked(MyGUI::Widget *sender) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 60f90c3af..c85e7eca7 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include @@ -177,7 +176,7 @@ namespace MWGui mHeadRotate->setScrollPosition(initialPos); onHeadRotate(mHeadRotate, initialPos); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mRaceList); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mRaceList); } void RaceDialog::setRaceId(const std::string &raceId) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 903fdf69d..e9fb2a964 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -94,7 +94,7 @@ namespace MWGui MWBase::Environment::get().getStateManager()->deleteGame (mCurrentCharacter, mCurrentSlot); mSaveList->removeItemAt(mSaveList->getIndexSelected()); onSlotSelected(mSaveList, mSaveList->getIndexSelected()); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mSaveList); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); if (mSaveList->getItemCount() == 0) { @@ -114,7 +114,7 @@ namespace MWGui void SaveGameDialog::onDeleteSlotCancel() { - MyGUI::InputManager::getInstance().setKeyFocusWidget(mSaveList); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); } void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender) @@ -138,9 +138,9 @@ namespace MWGui mSaveNameEdit->setCaption (""); if (mSaving) - MyGUI::InputManager::getInstance().setKeyFocusWidget(mSaveNameEdit); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit); else - MyGUI::InputManager::getInstance().setKeyFocusWidget(mSaveList); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); center(); @@ -244,7 +244,7 @@ namespace MWGui void SaveGameDialog::onConfirmationCancel() { - MyGUI::InputManager::getInstance().setKeyFocusWidget(mSaveList); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); } void SaveGameDialog::accept(bool reallySure) @@ -331,7 +331,7 @@ namespace MWGui void SaveGameDialog::onCharacterAccept(MyGUI::ComboBox* sender, size_t pos) { // Give key focus to save list so we can confirm the selection with Enter - MyGUI::InputManager::getInstance().setKeyFocusWidget(mSaveList); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); } void SaveGameDialog::fillSaveList() diff --git a/apps/openmw/mwgui/screenfader.cpp b/apps/openmw/mwgui/screenfader.cpp index e75e4954e..619852a22 100644 --- a/apps/openmw/mwgui/screenfader.cpp +++ b/apps/openmw/mwgui/screenfader.cpp @@ -38,12 +38,20 @@ namespace MWGui if (!mRunning) return; - if (mRemainingTime <= 0 || mStartAlpha == mTargetAlpha) + if (mStartAlpha == mTargetAlpha) { finish(); return; } + if (mRemainingTime <= 0) + { + // Make sure the target alpha is applied + mFader->notifyAlphaChanged(mTargetAlpha); + finish(); + return; + } + if (mRemainingTime > mTargetTime) { mRemainingTime -= dt; @@ -162,7 +170,7 @@ namespace MWGui if (time < 0.f) return; - if (time == 0.f) + if (time == 0.f && delay == 0.f) { mCurrentAlpha = targetAlpha; applyAlpha(); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index adbbaba15..f2c967da4 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -1,7 +1,6 @@ #include "scrollwindow.hpp" #include -#include #include #include @@ -67,7 +66,7 @@ namespace MWGui setTakeButtonShow(showTakeButton); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); } void ScrollWindow::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 9cb60af1a..85001dece 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include @@ -631,7 +630,7 @@ namespace MWGui highlightCurrentResolution(); updateControlsBox(); resetScrollbars(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mOkButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton); } void SettingsWindow::onWindowResize(MyGUI::Window *_sender) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 018d12c92..5864a5451 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -458,7 +457,7 @@ namespace MWGui void SpellCreationDialog::onOpen() { center(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mNameEdit); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); } void SpellCreationDialog::onReferenceUnavailable () diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 36d914a00..ecf6d0867 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -94,7 +94,7 @@ namespace MWGui // Reset the filter focus when opening the window MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); if (focus == mFilterEdit) - MyGUI::InputManager::getInstance().resetKeyFocusWidget(); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); updateSpells(); } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 91d414af3..8998e3a80 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -40,8 +40,8 @@ namespace MWGui , mBounty(0) , mSkillWidgets() , mChanged(true) + , mMinFullWidth(mMainWidget->getSize().width) { - setCoord(0,0,498, 342); const char *names[][2] = { @@ -88,8 +88,41 @@ namespace MWGui void StatsWindow::onWindowResize(MyGUI::Window* window) { - mLeftPane->setCoord( MyGUI::IntCoord(0, 0, static_cast(0.44*window->getSize().width), window->getSize().height) ); - mRightPane->setCoord( MyGUI::IntCoord(static_cast(0.44*window->getSize().width), 0, static_cast(0.56*window->getSize().width), window->getSize().height) ); + int windowWidth = window->getSize().width; + int windowHeight = window->getSize().height; + + //initial values defined in openmw_stats_window.layout, if custom options are not present in .layout, a default is loaded + float leftPaneRatio = 0.44; + if (mLeftPane->isUserString("LeftPaneRatio")) + leftPaneRatio = MyGUI::utility::parseFloat(mLeftPane->getUserString("LeftPaneRatio")); + + int leftOffsetWidth = 24; + if (mLeftPane->isUserString("LeftOffsetWidth")) + leftOffsetWidth = MyGUI::utility::parseInt(mLeftPane->getUserString("LeftOffsetWidth")); + + float rightPaneRatio = 1.f - leftPaneRatio; + int minLeftWidth = static_cast(mMinFullWidth * leftPaneRatio); + int minLeftOffsetWidth = minLeftWidth + leftOffsetWidth; + + //if there's no space for right pane + mRightPane->setVisible(windowWidth >= minLeftOffsetWidth); + if (!mRightPane->getVisible()) + { + mLeftPane->setCoord(MyGUI::IntCoord(0, 0, windowWidth - leftOffsetWidth, windowHeight)); + } + //if there's some space for right pane + else if (windowWidth < mMinFullWidth) + { + mLeftPane->setCoord(MyGUI::IntCoord(0, 0, minLeftWidth, windowHeight)); + mRightPane->setCoord(MyGUI::IntCoord(minLeftWidth, 0, windowWidth - minLeftWidth, windowHeight)); + } + //if there's enough space for both panes + else + { + mLeftPane->setCoord(MyGUI::IntCoord(0, 0, static_cast(leftPaneRatio*windowWidth), windowHeight)); + mRightPane->setCoord(MyGUI::IntCoord(static_cast(leftPaneRatio*windowWidth), 0, static_cast(rightPaneRatio*windowWidth), windowHeight)); + } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), mSkillView->getCanvasSize().height); diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 8dab2f3d9..7d1be6a15 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -69,6 +69,7 @@ namespace MWGui std::set mExpelled; bool mChanged; + const int mMinFullWidth; protected: virtual void onPinToggled(); diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 4cc7a576b..54f2d3be9 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -5,7 +5,6 @@ #include #include -#include namespace MWGui { @@ -24,7 +23,7 @@ namespace MWGui okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } void TextInputDialog::setNextButtonShow(bool shown) @@ -47,7 +46,7 @@ namespace MWGui { WindowModal::onOpen(); // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } // widget controls @@ -57,7 +56,7 @@ namespace MWGui if (mTextEdit->getCaption() == "") { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget (mTextEdit); } else eventDone(this); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 83096140e..0c9b31b65 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -136,7 +136,7 @@ namespace MWGui onFilterChanged(mFilterAll); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTotalBalance); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTotalBalance); } void TradeWindow::onFrame(float dt) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 7ec8f814a..18c4eec87 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -104,9 +104,9 @@ namespace MWGui } if (mUntilHealedButton->getVisible()) - MyGUI::InputManager::getInstance().setKeyFocusWidget(mUntilHealedButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton); else - MyGUI::InputManager::getInstance().setKeyFocusWidget(mWaitButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mWaitButton); } bool WaitDialog::exit() @@ -261,7 +261,7 @@ namespace MWGui { mHourText->setCaptionWithReplacing (MyGUI::utility::toString(position+1) + " #{sRestMenu2}"); mManualHours = position+1; - MyGUI::InputManager::getInstance().setKeyFocusWidget(mWaitButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mWaitButton); } void WaitDialog::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2f7a4f10c..962d4d044 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -718,7 +718,7 @@ namespace MWGui setCursorVisible(!gameMode); if (gameMode) - MyGUI::InputManager::getInstance().resetKeyFocusWidget(); + setKeyFocusWidget (nullptr); // Icons of forced hidden windows are displayed setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); @@ -1804,6 +1804,13 @@ namespace MWGui } } + // Remove this wrapper once onKeyFocusChanged call is rendered unnecessary + void WindowManager::setKeyFocusWidget(MyGUI::Widget *widget) + { + MyGUI::InputManager::getInstance().setKeyFocusWidget(widget); + onKeyFocusChanged(widget); + } + void WindowManager::onKeyFocusChanged(MyGUI::Widget *widget) { if (widget && widget->castType(false)) @@ -1997,7 +2004,7 @@ namespace MWGui sizeVideo(screenSize.width, screenSize.height); MyGUI::Widget* oldKeyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mVideoWidget); + setKeyFocusWidget(mVideoWidget); mVideoBackground->setVisible(true); @@ -2035,7 +2042,7 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->resumeSounds(); - MyGUI::InputManager::getInstance().setKeyFocusWidget(oldKeyFocus); + setKeyFocusWidget(oldKeyFocus); setCursorVisible(cursorWasVisible); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index fea01b5b8..c5d78e504 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -147,6 +147,9 @@ namespace MWGui /// (and will continually update the window while doing so) virtual void playVideo(const std::string& name, bool allowSkipping); + /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. + virtual void setKeyFocusWidget (MyGUI::Widget* widget); + virtual void setNewGame(bool newgame); virtual void pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 590895658..8456643d0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -89,6 +89,7 @@ namespace MWInput , mSneaking(false) , mAttemptJump(false) , mInvUiScalingFactor(1.f) + , mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input")) , mFakeDeviceID(1) { mInputManager = new SDLUtil::InputWrapper(window, viewer, grab); @@ -570,9 +571,9 @@ 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 xmove = xAxis * dt * 1500.0f * mInvUiScalingFactor; - float ymove = yAxis * dt * 1500.0f * mInvUiScalingFactor; - if (xmove != 0|| ymove != 0) + float xmove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; + float ymove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; + if (xmove != 0|| ymove != 0 || zAxis != 0) { mGuiCursorX += xmove; mGuiCursorY += ymove; @@ -635,11 +636,7 @@ namespace MWInput mPlayer->setAutoMove (false); mPlayer->setForwardBackward((yAxis - 0.5f) * 2 * -1); } - else if(mPlayer->getAutoMove()) - { - triedToMove = true; - mPlayer->setForwardBackward (1); - } + if (triedToMove) mJoystickLastUsed = true; @@ -661,7 +658,8 @@ namespace MWInput mPlayer->setAutoMove (false); mPlayer->setForwardBackward (actionIsActive(A_MoveForward) ? 1 : -1); } - else if(mPlayer->getAutoMove()) + + if (mPlayer->getAutoMove()) { alwaysRunAllowed = true; triedToMove = true; diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 8b3253dcd..caf57681d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -209,6 +209,7 @@ namespace MWInput std::map mControlSwitch; float mInvUiScalingFactor; + float mGamepadCursorSpeed; private: void convertMousePosForMyGUI(int& x, int& y); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ce0ab3890..2928da1a5 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -594,7 +594,7 @@ namespace MWMechanics void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) { CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature); - if (creatureStats.isDead()) + if (creatureStats.isDeathAnimationFinished()) return; MagicEffects now = creatureStats.getSpells().getMagicEffects(); @@ -1766,7 +1766,8 @@ namespace MWMechanics else if (!isPlayer) iter->first.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor); - if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) + const bool isDead = iter->first.getClass().getCreatureStats(iter->first).isDead(); + if (!isDead && iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) ctrl->skipAnim(); // Handle player last, in case a cell transition occurs by casting a teleportation spell @@ -1851,10 +1852,6 @@ namespace MWMechanics stats.getActiveSpells().visitEffectSources(soulTrap); } - // Reset magic effects and recalculate derived effects - // One case where we need this is to make sure bound items are removed upon death - stats.modifyMagicEffects(MWMechanics::MagicEffects()); - stats.getActiveSpells().clear(); calculateCreatureStatModifiers(iter->first, 0); if (cls.isEssential(iter->first)) @@ -1896,6 +1893,10 @@ namespace MWMechanics End of tes3mp addition */ + // Reset magic effects and recalculate derived effects + // One case where we need this is to make sure bound items are removed upon death + stats.modifyMagicEffects(MWMechanics::MagicEffects()); + stats.getActiveSpells().clear(); // Make sure spell effects are removed purgeSpellEffects(stats.getActorId()); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index aa7b4fe05..ae6727063 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -129,6 +129,9 @@ namespace MWMechanics || target.getClass().getCreatureStats(target).isDead()) return true; + if (actor == target) // This should never happen. + return true; + if (!storage.isFleeing()) { if (storage.mCurrentAction.get()) // need to wait to init action with its attack range diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 96ff0c308..167e13128 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -422,6 +422,11 @@ namespace MWMechanics return true; } + if (actor.getClass().isPureLandCreature(actor) && MWBase::Environment::get().getWorld()->isWalkingOnWater(enemy)) + { + return false; + } + if (actor.getClass().isPureFlyingCreature(actor) || actor.getClass().isPureLandCreature(actor)) { if (MWBase::Environment::get().getWorld()->isSwimming(enemy)) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 018756aea..d97edfe2a 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -88,6 +88,7 @@ namespace MWMechanics // that is the user's responsibility MWBase::Environment::get().getWorld()->moveObject(actor, mX, mY, mZ); actor.getClass().adjustPosition(actor, false); + reset(); } void AiTravel::writeState(ESM::AiSequence::AiSequence &sequence) const diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index dc225ac23..d00bbeb5f 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -527,7 +527,8 @@ namespace MWMechanics if (greetingState == AiWanderStorage::Greet_None) { if ((playerPos - actorPos).length2() <= helloDistance*helloDistance && - !player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor) + !player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed() + && MWBase::Environment::get().getWorld()->getLOS(player, actor) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor)) greetingTimer++; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 71f829a56..9b60987f8 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1013,7 +1013,6 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: if(evt.compare(0, 7, "sound: ") == 0) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->stopSound3D(mPtr, evt.substr(7)); sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); return; } @@ -1054,7 +1053,6 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } else { - sndMgr->stopSound3D(mPtr, sound); sndMgr->playSound3D(mPtr, sound, volume, pitch); } } @@ -1165,8 +1163,8 @@ void CharacterController::updateIdleStormState(bool inwater) { if (!mAnimation->isPlaying("idlestorm")) { - mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::BlendMask_RightArm, true, - 1.0f, "start", "stop", 0.0f, ~0ul); + int mask = MWRender::Animation::BlendMask_Torso | MWRender::Animation::BlendMask_RightArm; + mAnimation->play("idlestorm", Priority_Storm, mask, true, 1.0f, "start", "stop", 0.0f, ~0ul); } else { @@ -2791,7 +2789,7 @@ void CharacterController::updateContinuousVfx() for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) { - if (mPtr.getClass().getCreatureStats(mPtr).isDead() + if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() || mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(*it)).getMagnitude() <= 0) mAnimation->removeEffect(*it); } @@ -2818,14 +2816,14 @@ void CharacterController::setVisibility(float visibility) if (mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Invisibility).getModifier()) // Ignore base magnitude (see bug #3555). { if (mPtr == getPlayer()) - alpha = 0.4f; + alpha = 0.25f; else - alpha = 0.f; + alpha = 0.05f; } float chameleon = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude(); if (chameleon) { - alpha *= std::max(0.2f, (100.f - chameleon)/100.f); + alpha *= std::min(0.75f, std::max(0.25f, (100.f - chameleon)/100.f)); } visibility = std::min(visibility, alpha); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 8f0f1a74a..50096cf7b 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -154,16 +154,12 @@ namespace MWMechanics End of tes3mp change (major) */ - if (!(weapon.isEmpty() && !attacker.getClass().isNpc())) // Unarmed creature attacks don't affect armor condition - { - // Reduce shield durability by incoming damage - int shieldhealth = shield->getClass().getItemHealth(*shield); - - shieldhealth -= std::min(shieldhealth, int(damage)); - shield->getCellRef().setCharge(shieldhealth); - if (shieldhealth == 0) - inv.unequipItem(*shield, blocker); - } + // Reduce shield durability by incoming damage + int shieldhealth = shield->getClass().getItemHealth(*shield); + shieldhealth -= std::min(shieldhealth, int(damage)); + shield->getCellRef().setCharge(shieldhealth); + if (shieldhealth == 0) + inv.unequipItem(*shield, blocker); // Reduce blocker fatigue const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->mValue.getFloat(); const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->mValue.getFloat(); @@ -559,7 +555,7 @@ namespace MWMechanics if(sound) sndMgr->playSound3D(victim, sound->mId, 1.0f, 1.0f); } - else + else if (!healthdmg) sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f); } diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 0d3b1560c..1efae92ba 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -131,10 +131,7 @@ namespace MWMechanics void Enchanting::nextCastStyle() { if (itemEmpty()) - { - mCastStyle = ESM::Enchantment::WhenUsed; return; - } const bool powerfulSoul = getGemCharge() >= \ MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->mValue.getInteger(); @@ -296,6 +293,8 @@ namespace MWMechanics void Enchanting::setEnchanter(const MWWorld::Ptr& enchanter) { mEnchanter = enchanter; + // Reset cast style + mCastStyle = ESM::Enchantment::CastOnce; } int Enchanting::getEnchantChance() const diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index f2771310f..f01b4ea7b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1740,18 +1740,28 @@ namespace MWMechanics void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) { - MWMechanics::AiSequence& aiSequence = ptr.getClass().getCreatureStats(ptr).getAiSequence(); + CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); - if (aiSequence.isInCombat(target)) + // Don't add duplicate packages nor add packages to dead actors. + if (stats.isDead() || stats.getAiSequence().isInCombat(target)) return; - aiSequence.stack(MWMechanics::AiCombat(target), ptr); + // The target is somehow the same as the actor. Early-out. + if (ptr == target) + { + // We don't care about dialogue filters since the target is invalid. + // We still want to play the combat taunt. + MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); + return; + } + + stats.getAiSequence().stack(MWMechanics::AiCombat(target), ptr); if (target == getPlayer()) { // if guard starts combat with player, guards pursuing player should do the same if (ptr.getClass().isClass(ptr, "Guard")) { - ptr.getClass().getCreatureStats(ptr).setHitAttemptActorId(target.getClass().getCreatureStats(target).getActorId()); // Stops guard from ending combat if player is unreachable + stats.setHitAttemptActorId(target.getClass().getCreatureStats(target).getActorId()); // Stops guard from ending combat if player is unreachable for (Actors::PtrActorMap::const_iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { if (iter->first.getClass().isClass(iter->first, "Guard")) @@ -1769,8 +1779,7 @@ namespace MWMechanics } // Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly - if (ptr.getClass().isNpc() && !ptr.getClass().getCreatureStats(ptr).isDead()) - MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); + MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); } void MechanicsManager::getObjectsInRange(const osg::Vec3f &position, float radius, std::vector &objects) @@ -1869,8 +1878,8 @@ namespace MWMechanics if (ptr.getClass().isNpc()) disposition = getDerivedDisposition(ptr, true); - int fight = std::max(0, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() - + static_cast(getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast(disposition)))); + int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() + + static_cast(getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast(disposition))); if (ptr.getClass().isNpc() && target.getClass().isNpc()) { diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index d4a393c72..9e05509f1 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -106,7 +106,7 @@ bool Objects::onOpen(const MWWorld::Ptr& ptr) PtrControllerMap::iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) return iter->second->onOpen(); - return false; + return true; } void Objects::onClose(const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 65b7ad531..04abb5344 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -951,21 +951,24 @@ namespace MWMechanics if (item.getCellRef().getEnchantmentCharge() < castCost) { if (mCaster == getPlayer()) + { MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); - // Failure sound - int school = 0; - if (!enchantment->mEffects.mList.empty()) - { - short effectId = enchantment->mEffects.mList.front().mEffectID; - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectId); - school = magicEffect->mData.mSchool; + // Failure sound + int school = 0; + if (!enchantment->mEffects.mList.empty()) + { + short effectId = enchantment->mEffects.mList.front().mEffectID; + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectId); + school = magicEffect->mData.mSchool; + } + + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f); } - static const std::string schools[] = { - "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" - }; - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f); return false; } // Reduce charge diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 6d37949f9..df21deff6 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1215,21 +1215,29 @@ namespace MWScript std::string targetId = ::Misc::StringUtils::lowerCase(runtime.getStringLiteral (runtime[0].mInteger)); runtime.pop(); - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find (spellId); + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId); if (!spell) { - runtime.getContext().report("spellcasting failed: can not find spell \""+spellId+"\""); + runtime.getContext().report("spellcasting failed: cannot find spell \""+spellId+"\""); return; } if (spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power) { - runtime.getContext().report("spellcasting failed: you can cast only spells and powers."); + runtime.getContext().report("spellcasting failed: you can only cast spells and powers."); return; } - // Obviously we can not use casting animation for player here - if (ptr.getClass().isActor() && ptr != MWMechanics::getPlayer()) + if (ptr == MWMechanics::getPlayer()) + { + MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); + store.setSelectedEnchantItem(store.end()); + MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, ptr))); + MWBase::Environment::get().getWindowManager()->updateSpellWindow(); + return; + } + + if (ptr.getClass().isActor()) { MWMechanics::AiCast castPackage(targetId, spellId, true); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(castPackage, ptr); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 77f25f326..aa1686f7c 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -747,6 +747,9 @@ namespace MWSound void SoundManager::stopSound(const std::string& soundId) { + if(!mOutput->isInitialized()) + return; + Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); if (!sfx) return; @@ -755,6 +758,9 @@ namespace MWSound void SoundManager::stopSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId) { + if(!mOutput->isInitialized()) + return; + Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); if (!sfx) return; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 56b46d856..833353174 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -169,8 +169,8 @@ void ESMStore::validate() if (!fact) { Log(Debug::Verbose) << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent faction '" << npc.mFaction << "', ignoring it."; - npc.mFaction = ""; - npc.mNpdt.mRank = -1; + npc.mFaction.clear(); + npc.mNpdt.mRank = 0; changed = true; } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c17c69229..6bdfaf0be 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3175,6 +3175,7 @@ namespace MWWorld /// \note Using _any_ door pointed to the interior, /// not the one pointed to current door. pos = destDoor.mRef.getDoorDest(); + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; return true; } } @@ -3185,6 +3186,7 @@ namespace MWWorld if (!statics.empty()) { pos = statics.begin()->mRef.getPosition(); + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; return true; } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index e66f051d7..7ccfb9285 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -33,7 +33,7 @@ namespace Compiler { extensions.registerInstruction ("aiactivate", "c/l", opcodeAIActivate, opcodeAIActivateExplicit); - extensions.registerInstruction ("aitravel", "fff/lx", opcodeAiTravel, + extensions.registerInstruction ("aitravel", "fff/zx", opcodeAiTravel, opcodeAiTravelExplicit); extensions.registerInstruction ("aiescort", "cffff/l", opcodeAiEscort, opcodeAiEscortExplicit); @@ -340,7 +340,7 @@ namespace Compiler extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase); extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase); extensions.registerFunction ("getcurrentweather", 'l', "", opcodeGetCurrentWeather); - extensions.registerInstruction ("modregion", "S/llllllllll", opcodeModRegion); + extensions.registerInstruction ("modregion", "S/llllllllllX", opcodeModRegion); } } diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 7a4558d57..390dde223 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -428,7 +428,7 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) { QFileInfo info(dir.absoluteFilePath(path2)); - if (item(info.absoluteFilePath()) != 0) + if (item(info.fileName())) continue; try { diff --git a/components/esm/custommarkerstate.hpp b/components/esm/custommarkerstate.hpp index fc9286bfe..2be43c53b 100644 --- a/components/esm/custommarkerstate.hpp +++ b/components/esm/custommarkerstate.hpp @@ -16,7 +16,7 @@ struct CustomMarker std::string mNote; - bool operator == (const CustomMarker& other) + bool operator == (const CustomMarker& other) const { return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY; } diff --git a/components/esm/loadclot.hpp b/components/esm/loadclot.hpp index a9ea3e70a..4db791c0c 100644 --- a/components/esm/loadclot.hpp +++ b/components/esm/loadclot.hpp @@ -40,7 +40,7 @@ struct Clothing int mType; float mWeight; unsigned short mValue; - short mEnchant; + unsigned short mEnchant; }; CTDTstruct mData; diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index 8b48b71b7..dfe2b695a 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -59,7 +59,7 @@ struct Weapon short mType; unsigned short mHealth; float mSpeed, mReach; - short mEnchant; // Enchantment points. The real value is mEnchant/10.f + unsigned short mEnchant; // Enchantment points. The real value is mEnchant/10.f unsigned char mChop[2], mSlash[2], mThrust[2]; // Min and max int mFlags; }; // 32 bytes diff --git a/components/fallback/fallback.cpp b/components/fallback/fallback.cpp index e2060935e..d5c6c4e97 100644 --- a/components/fallback/fallback.cpp +++ b/components/fallback/fallback.cpp @@ -30,8 +30,8 @@ namespace Fallback { try { - // We have to rely on Boost because std::stof from C++11 - // uses the current locale for separators which we don't want and often silently ignores parsing errors. + // We have to rely on Boost because std::stof from C++11 uses the current locale + // for separators (which is undesired) and it often silently ignores parsing errors. return boost::lexical_cast(fallback); } catch (boost::bad_lexical_cast&) diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 3743eef4c..5f34539d5 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -50,6 +51,9 @@ namespace Files LinuxPath::LinuxPath(const std::string& application_name) : mName(application_name) { + boost::filesystem::path localPath = getLocalPath(); + if (chdir(localPath.string().c_str()) != 0) + Log(Debug::Warning) << "Error " << errno << " when changing current directory"; } boost::filesystem::path LinuxPath::getUserConfigPath() const @@ -75,7 +79,20 @@ boost::filesystem::path LinuxPath::getGlobalConfigPath() const boost::filesystem::path LinuxPath::getLocalPath() const { - return boost::filesystem::path("./"); + boost::filesystem::path localPath("./"); + std::string binPath(pathconf(".", _PC_PATH_MAX), '\0'); + const char *statusPaths[] = {"/proc/self/exe", "/proc/self/file", "/proc/curproc/exe", "/proc/curproc/file"}; + + for(const char *path : statusPaths) + { + if (readlink(path, &binPath[0], binPath.size()) != -1) + { + localPath = boost::filesystem::path(binPath).parent_path(); + break; + } + } + + return localPath; } boost::filesystem::path LinuxPath::getGlobalDataPath() const diff --git a/components/files/memorystream.hpp b/components/files/memorystream.hpp index 9a3510044..b4c7b7675 100644 --- a/components/files/memorystream.hpp +++ b/components/files/memorystream.hpp @@ -9,11 +9,31 @@ namespace Files struct MemBuf : std::streambuf { MemBuf(char const* buffer, size_t size) - { // a streambuf isn't specific to istreams, so we need a non-const pointer :/ - char* nonconstBuffer = (const_cast(buffer)); - this->setg(nonconstBuffer, nonconstBuffer, nonconstBuffer + size); + : bufferStart(const_cast(buffer)) + , bufferEnd(bufferStart + size) + { + this->setg(bufferStart, bufferStart, bufferEnd); + } + + pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) override + { + if (dir == std::ios_base::cur) + gbump(off); + else + setg(bufferStart, (dir == std::ios_base::beg ? bufferStart : bufferEnd) + off, bufferEnd); + + return gptr() - bufferStart; } + + pos_type seekpos(pos_type pos, std::ios_base::openmode which) override + { + return seekoff(pos, std::ios_base::beg, which); + } + + protected: + char* bufferStart; + char* bufferEnd; }; /// @brief A variant of std::istream that reads from a constant in-memory buffer. diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 2354e6f31..516f26021 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -11,6 +11,8 @@ #include namespace bconv = boost::locale::conv; +#include + /** * FIXME: Someone with Windows system should check this and correct if necessary * FIXME: MAX_PATH is irrelevant for extended-length paths, i.e. \\?\... @@ -33,6 +35,10 @@ WindowsPath::WindowsPath(const std::string& application_name) See boost::filesystem and boost::locale reference for details. */ boost::filesystem::path::imbue(boost::locale::generator().generate("")); + + boost::filesystem::path localPath = getLocalPath(); + if (!SetCurrentDirectoryA(localPath.string().c_str())) + Log(Debug::Warning) << "Error " << GetLastError() << " when changing current directory"; } boost::filesystem::path WindowsPath::getUserConfigPath() const @@ -73,7 +79,17 @@ boost::filesystem::path WindowsPath::getGlobalConfigPath() const boost::filesystem::path WindowsPath::getLocalPath() const { - return boost::filesystem::path("./"); + boost::filesystem::path localPath("./"); + WCHAR path[MAX_PATH + 1]; + memset(path, 0, sizeof(path)); + + if (GetModuleFileNameW(nullptr, path, MAX_PATH + 1) > 0) + { + localPath = boost::filesystem::path(bconv::utf_to_utf(path)).parent_path(); + } + + // lookup exe path + return localPath; } boost::filesystem::path WindowsPath::getGlobalDataPath() const diff --git a/components/nif/node.hpp b/components/nif/node.hpp index d9afbbed7..cc1871d83 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -238,10 +238,12 @@ struct NiRotatingParticles : Node // A node used as the base to switch between child nodes, such as for LOD levels. struct NiSwitchNode : public NiNode { + unsigned int initialIndex; + void read(NIFStream *nif) { NiNode::read(nif); - nif->getInt(); // unknown + initialIndex = nif->getUInt(); } }; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index d6a459b1b..9b31a1b93 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -400,7 +400,10 @@ namespace NifOsg return; } - osg::ref_ptr texture2d (new osg::Texture2D(handleSourceTexture(textureEffect->texture.getPtr(), imageManager))); + osg::ref_ptr image (handleSourceTexture(textureEffect->texture.getPtr(), imageManager)); + osg::ref_ptr texture2d (new osg::Texture2D(image)); + if (image) + texture2d->setTextureSize(image->s(), image->t()); texture2d->setName("envMap"); unsigned int clamp = static_cast(textureEffect->clamp); int wrapT = (clamp) & 0x1; @@ -585,8 +588,11 @@ namespace NifOsg { const Nif::NiTriShape* triShape = static_cast(nifNode); const std::string nodeName = Misc::StringUtils::lowerCase(triShape->name); - static const std::string pattern = "tri editormarker"; - if (!hasMarkers || nodeName.compare(0, pattern.size(), pattern) != 0) + static const std::string markerName = "tri editormarker"; + static const std::string shadowName = "shadow"; + static const std::string shadowName2 = "tri shadow"; + const bool isMarker = hasMarkers && !nodeName.compare(0, markerName.size(), markerName); + if (!isMarker && nodeName.compare(0, shadowName.size(), shadowName) && nodeName.compare(0, shadowName2.size(), shadowName2)) { if (triShape->skin.empty()) handleTriShape(triShape, node, composite, boundTextures, animflags); @@ -629,10 +635,8 @@ namespace NifOsg if (nifNode->recType == Nif::RC_NiSwitchNode) { - // show only first child by default - node->asSwitch()->setSingleChildOn(0); - const Nif::NiSwitchNode* niSwitchNode = static_cast(nifNode); + node->asSwitch()->setSingleChildOn(niSwitchNode->initialIndex); if (niSwitchNode->name == Constants::NightDayLabel && !SceneUtil::hasUserDescription(rootNode, Constants::NightDayLabel)) rootNode->getOrCreateUserDataContainer()->addDescription(Constants::NightDayLabel); else if (niSwitchNode->name == Constants::HerbalismLabel && !SceneUtil::hasUserDescription(rootNode, Constants::HerbalismLabel)) @@ -770,7 +774,10 @@ namespace NifOsg wrapT = inherit->getWrap(osg::Texture2D::WRAP_T); } - osg::ref_ptr texture (new osg::Texture2D(handleSourceTexture(st.getPtr(), imageManager))); + osg::ref_ptr image (handleSourceTexture(st.getPtr(), imageManager)); + osg::ref_ptr texture (new osg::Texture2D(image)); + if (image) + texture->setTextureSize(image->s(), image->t()); texture->setWrap(osg::Texture::WRAP_S, wrapS); texture->setWrap(osg::Texture::WRAP_T, wrapT); textures.push_back(texture); @@ -1337,6 +1344,8 @@ namespace NifOsg const Nif::NiSourceTexture *st = tex.texture.getPtr(); osg::ref_ptr image = handleSourceTexture(st, imageManager); texture2d = new osg::Texture2D(image); + if (image) + texture2d->setTextureSize(image->s(), image->t()); } else texture2d = new osg::Texture2D; diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 7c568ff9d..e8ebed868 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -1802,12 +1802,11 @@ bool Optimizer::MergeGeometryVisitor::mergePrimitive(osg::DrawElementsUInt& lhs, bool Optimizer::MergeGroupsVisitor::isOperationPermissible(osg::Group& node) { - return !node.asSwitch() && - !node.asTransform() && - !node.getCullCallback() && + return !node.getCullCallback() && !node.getEventCallback() && !node.getUpdateCallback() && - isOperationPermissibleForObject(&node); + isOperationPermissibleForObject(&node) && + typeid(node)==typeid(osg::Group); } void Optimizer::MergeGroupsVisitor::apply(osg::LOD &lod) diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index 59e75dc76..42f65fc55 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -7,6 +7,7 @@ #include #include +#include namespace Settings { @@ -354,12 +355,14 @@ float Manager::getFloat (const std::string& setting, const std::string& category const std::string value = getString(setting, category); try { - return std::stof(value); + // We have to rely on Boost because std::stof from C++11 uses the current locale + // for separators (which is undesired) and it often silently ignores parsing errors. + return boost::lexical_cast(value); } - catch(const std::exception& e) + catch (boost::bad_lexical_cast&) { Log(Debug::Warning) << "Cannot parse setting '" << setting << "' (invalid setting value: " << value << ")."; - return 0; + return 0.f; } } @@ -401,12 +404,16 @@ void Manager::setString(const std::string &setting, const std::string &category, void Manager::setInt (const std::string& setting, const std::string& category, const int value) { - setString(setting, category, std::to_string(value)); + std::ostringstream stream; + stream << value; + setString(setting, category, stream.str()); } void Manager::setFloat (const std::string &setting, const std::string &category, const float value) { - setString(setting, category, std::to_string(value)); + std::ostringstream stream; + stream << value; + setString(setting, category, stream.str()); } void Manager::setBool(const std::string &setting, const std::string &category, const bool value) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 63c676118..e40cc255b 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -163,6 +163,7 @@ namespace Shader if (image) { osg::ref_ptr normalMapTex (new osg::Texture2D(image)); + normalMapTex->setTextureSize(image->s(), image->t()); normalMapTex->setWrap(osg::Texture::WRAP_S, diffuseMap->getWrap(osg::Texture::WRAP_S)); normalMapTex->setWrap(osg::Texture::WRAP_T, diffuseMap->getWrap(osg::Texture::WRAP_T)); normalMapTex->setFilter(osg::Texture::MIN_FILTER, diffuseMap->getFilter(osg::Texture::MIN_FILTER)); @@ -186,7 +187,9 @@ namespace Shader boost::replace_last(specularMapFileName, ".", mSpecularMapPattern + "."); if (mImageManager.getVFS()->exists(specularMapFileName)) { - osg::ref_ptr specularMapTex (new osg::Texture2D(mImageManager.getImage(specularMapFileName))); + osg::ref_ptr image (mImageManager.getImage(specularMapFileName)); + osg::ref_ptr specularMapTex (new osg::Texture2D(image)); + specularMapTex->setTextureSize(image->s(), image->t()); specularMapTex->setWrap(osg::Texture::WRAP_S, diffuseMap->getWrap(osg::Texture::WRAP_S)); specularMapTex->setWrap(osg::Texture::WRAP_T, diffuseMap->getWrap(osg::Texture::WRAP_T)); specularMapTex->setFilter(osg::Texture::MIN_FILTER, diffuseMap->getFilter(osg::Texture::MIN_FILTER)); diff --git a/docs/source/reference/modding/extended.rst b/docs/source/reference/modding/extended.rst index d7c199b57..9d71f384d 100644 --- a/docs/source/reference/modding/extended.rst +++ b/docs/source/reference/modding/extended.rst @@ -53,7 +53,7 @@ It is also possible to attach opening/closing sounds to container's animations: :: - 1.0: ContainerClose: open + 1.0: ContainerClose: start 1.01: Sound: AC_dw_drawer_close 2.0: ContainerClose: stop diff --git a/docs/source/reference/modding/settings/input.rst b/docs/source/reference/modding/settings/input.rst index 51c72e15d..d481321c2 100644 --- a/docs/source/reference/modding/settings/input.rst +++ b/docs/source/reference/modding/settings/input.rst @@ -133,3 +133,16 @@ which are always sent if a controller is present and detected. Disabling this setting can be useful for working around controller-related issues or for setting up split-screen gameplay configurations. This setting can be toggled in game with the Enable Joystick button in the Controls panel of the Options menu. + +gamepad cursor speed +-------------------- + +:Type: float +:Range: >0 +:Default: 1.0 + +This setting controls the speed of the cursor within GUI mode when using the joystick. +This setting has no effect on the camera rotation speed, which is controlled by the +camera sensitivity setting. + +This setting can only be configured by editing the settings configuration file. diff --git a/docs/source/reference/modding/settings/video.rst b/docs/source/reference/modding/settings/video.rst index a8a95739e..040b9948f 100644 --- a/docs/source/reference/modding/settings/video.rst +++ b/docs/source/reference/modding/settings/video.rst @@ -140,7 +140,7 @@ framerate limit :Type: floating point :Range: >= 0.0 -:Default: 0.0 +:Default: 300 This setting determines the maximum frame rate in frames per second. If this setting is 0.0, the frame rate is unlimited. @@ -159,8 +159,6 @@ in the sense that enabling vertical sync limits the frame rate to the refresh ra Choosing to limit the frame rate using this setting instead of vsync may reduce input lag due to the game not having to wait for the vertical blanking interval. -This setting can only be configured by editing the settings configuration file. - contrast -------- diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index ccaeb0319..db2c722d7 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -19,6 +19,11 @@ + + + + + @@ -27,11 +32,6 @@ - - - - - @@ -61,6 +61,11 @@ + + + + + @@ -69,11 +74,6 @@ - - - - - diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout index f26f1d598..77de3ac75 100644 --- a/files/mygui/openmw_stats_window.layout +++ b/files/mygui/openmw_stats_window.layout @@ -2,9 +2,10 @@ - - + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index d3776e92f..f8c31eed7 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -364,6 +364,9 @@ invert y axis = false # Enable controller support. enable controller = true +# Emulated gamepad cursor sensitivity. +gamepad cursor speed = 1.0 + [Saves] # Name of last character played, and default for loading save files. @@ -442,7 +445,7 @@ antialiasing = 0 vsync = false # Maximum frames per second. 0.0 is unlimited, or >0.0 to limit. -framerate limit = 0.0 +framerate limit = 300 # Game video contrast. (>0.0). No effect in Linux. contrast = 1.0 diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui index 0afda6ac7..eba913b91 100644 --- a/files/ui/graphicspage.ui +++ b/files/ui/graphicspage.ui @@ -34,7 +34,7 @@ - Window border + Window Border @@ -62,6 +62,13 @@ + + + + Framerate Limit: + + + @@ -143,6 +150,31 @@ + + + + false + + + FPS + + + 1 + + + 1 + + + 1000 + + + 15 + + + 300 + + +