From 3d917fcbadac599d9acacb4624c62c169312582f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 25 Oct 2018 00:07:01 +0100 Subject: [PATCH 01/50] Add basic OpenGL debug callback --- apps/openmw/engine.cpp | 4 +++ components/CMakeLists.txt | 2 +- components/misc/gldebug.cpp | 60 +++++++++++++++++++++++++++++++++++++ components/misc/gldebug.hpp | 16 ++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 components/misc/gldebug.cpp create mode 100644 components/misc/gldebug.hpp diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2a4145c98..015ff635c 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -365,6 +366,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings) checkSDLError(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24)); + checkSDLError(SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG)); if (antialiasing > 0) { @@ -425,6 +427,8 @@ void OMW::Engine::createWindow(Settings::Manager& settings) camera->setGraphicsContext(graphicsWindow); camera->setViewport(0, 0, width, height); + mViewer->setRealizeOperation(new EnableGLDebugOperation()); + mViewer->realize(); mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, width, height); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index d26a92d44..ec0e511ca 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -86,7 +86,7 @@ add_component_dir (esmterrain ) add_component_dir (misc - constants utf8stream stringops resourcehelpers rng messageformatparser weakcache + constants utf8stream stringops resourcehelpers rng messageformatparser weakcache gldebug ) add_component_dir (debug diff --git a/components/misc/gldebug.cpp b/components/misc/gldebug.cpp new file mode 100644 index 000000000..71d75fa73 --- /dev/null +++ b/components/misc/gldebug.cpp @@ -0,0 +1,60 @@ +#include "gldebug.hpp" + +#include + +// OpenGL constants not provided by OSG: +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_TYPE_ERROR 0x824C + +void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) +{ + Log(Debug::Error) << message; +} + +void enableGLDebugExtension(unsigned int contextID) +{ + typedef void (GL_APIENTRY *DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); + typedef void (GL_APIENTRY *GLDebugMessageControlFunction)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + typedef void (GL_APIENTRY *GLDebugMessageCallbackFunction)(DEBUGPROC, const void* userParam); + + GLDebugMessageControlFunction glDebugMessageControl = nullptr; + GLDebugMessageCallbackFunction glDebugMessageCallback = nullptr; + + if (osg::isGLExtensionSupported(contextID, "GL_KHR_debug")) + { + osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallback"); + osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControl"); + } + else if (osg::isGLExtensionSupported(contextID, "GL_ARB_debug_output")) + { + osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackARB"); + osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlARB"); + } + else if (osg::isGLExtensionSupported(contextID, "GL_AMD_debug_output")) + { + osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackAMD"); + osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlAMD"); + } + + if (glDebugMessageCallback && glDebugMessageControl) + { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, true); + glDebugMessageCallback(debugCallback, nullptr); + + Log(Debug::Info) << "OpenGL debug callback attached."; + } +} + +EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation("EnableGLDebugOperation", false) +{ +} + +void EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsContext) +{ + OpenThreads::ScopedLock lock(mMutex); + + unsigned int contextID = graphicsContext->getState()->getContextID(); + enableGLDebugExtension(contextID); +} diff --git a/components/misc/gldebug.hpp b/components/misc/gldebug.hpp new file mode 100644 index 000000000..2401a53a4 --- /dev/null +++ b/components/misc/gldebug.hpp @@ -0,0 +1,16 @@ +#ifndef OPENMW_COMPONENTS_MISC_GLDEBUG_H +#define OPENMW_COMPONENTS_MISC_GLDEBUG_H + +#include + +class EnableGLDebugOperation : public osg::GraphicsOperation +{ +public: + EnableGLDebugOperation(); + + virtual void operator()(osg::GraphicsContext* graphicsContext); + +private: + OpenThreads::Mutex mMutex; +}; +#endif From aaa3eedf990d4988c328c107d8d33c4c21483959 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Oct 2018 15:09:08 +0100 Subject: [PATCH 02/50] Move gldebug from components/misc to components/debug --- components/{misc => debug}/gldebug.cpp | 0 components/{misc => debug}/gldebug.hpp | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename components/{misc => debug}/gldebug.cpp (100%) rename components/{misc => debug}/gldebug.hpp (100%) diff --git a/components/misc/gldebug.cpp b/components/debug/gldebug.cpp similarity index 100% rename from components/misc/gldebug.cpp rename to components/debug/gldebug.cpp diff --git a/components/misc/gldebug.hpp b/components/debug/gldebug.hpp similarity index 100% rename from components/misc/gldebug.hpp rename to components/debug/gldebug.hpp From d42c97685284df35a34d419d258ae15fc02ae96b Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Oct 2018 15:11:50 +0100 Subject: [PATCH 03/50] Add detailed OpenGL debug messages --- components/debug/gldebug.cpp | 63 +++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/components/debug/gldebug.cpp b/components/debug/gldebug.cpp index 71d75fa73..52a1bb5a6 100644 --- a/components/debug/gldebug.cpp +++ b/components/debug/gldebug.cpp @@ -3,13 +3,66 @@ #include // OpenGL constants not provided by OSG: -#define GL_DEBUG_OUTPUT 0x92E0 -#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 -#define GL_DEBUG_TYPE_ERROR 0x824C +#include void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) { - Log(Debug::Error) << message; + std::string srcStr; + switch (source) + { + case GL_DEBUG_SOURCE_API: + srcStr = "API"; + break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + srcStr = "WINDOW_SYSTEM"; + break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: + srcStr = "SHADER_COMPILER"; + break; + case GL_DEBUG_SOURCE_THIRD_PARTY: + srcStr = "THIRD_PARTY"; + break; + case GL_DEBUG_SOURCE_APPLICATION: + srcStr = "APPLICATION"; + break; + case GL_DEBUG_SOURCE_OTHER: + srcStr = "OTHER"; + break; + default: + srcStr = "UNDEFINED"; + break; + } + + std::string typeStr; + + Debug::Level logSeverity = Debug::Warning; + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + typeStr = "ERROR"; + logSeverity = Debug::Error; + break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + typeStr = "DEPRECATED_BEHAVIOR"; + break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + typeStr = "UNDEFINED_BEHAVIOR"; + break; + case GL_DEBUG_TYPE_PORTABILITY: + typeStr = "PORTABILITY"; + break; + case GL_DEBUG_TYPE_PERFORMANCE: + typeStr = "PERFORMANCE"; + break; + case GL_DEBUG_TYPE_OTHER: + typeStr = "OTHER"; + break; + default: + typeStr = "UNDEFINED"; + break; + } + + Log(logSeverity) << "OpenGL " << typeStr << " [" << srcStr << "]: " << message; } void enableGLDebugExtension(unsigned int contextID) @@ -45,6 +98,8 @@ void enableGLDebugExtension(unsigned int contextID) Log(Debug::Info) << "OpenGL debug callback attached."; } + else + Log(Debug::Error) << "Unable to attach OpenGL debug callback."; } EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation("EnableGLDebugOperation", false) From ac18983f37733d0d93d2e6f64d8211c41678222f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Oct 2018 15:18:38 +0100 Subject: [PATCH 04/50] Finish gldebug location move --- apps/openmw/engine.cpp | 4 ++-- components/CMakeLists.txt | 4 ++-- components/debug/gldebug.hpp | 21 ++++++++++++--------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 015ff635c..96fac86bc 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -11,9 +11,9 @@ #include #include +#include #include -#include #include #include @@ -427,7 +427,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings) camera->setGraphicsContext(graphicsWindow); camera->setViewport(0, 0, width, height); - mViewer->setRealizeOperation(new EnableGLDebugOperation()); + mViewer->setRealizeOperation(new Debug::EnableGLDebugOperation()); mViewer->realize(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index ec0e511ca..d57a61f46 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -86,11 +86,11 @@ add_component_dir (esmterrain ) add_component_dir (misc - constants utf8stream stringops resourcehelpers rng messageformatparser weakcache gldebug + constants utf8stream stringops resourcehelpers rng messageformatparser weakcache ) add_component_dir (debug - debugging debuglog + debugging debuglog gldebug ) IF(NOT WIN32 AND NOT APPLE) diff --git a/components/debug/gldebug.hpp b/components/debug/gldebug.hpp index 2401a53a4..77d6c82a8 100644 --- a/components/debug/gldebug.hpp +++ b/components/debug/gldebug.hpp @@ -1,16 +1,19 @@ -#ifndef OPENMW_COMPONENTS_MISC_GLDEBUG_H -#define OPENMW_COMPONENTS_MISC_GLDEBUG_H +#ifndef OPENMW_COMPONENTS_DEBUG_GLDEBUG_H +#define OPENMW_COMPONENTS_DEBUG_GLDEBUG_H #include -class EnableGLDebugOperation : public osg::GraphicsOperation +namespace Debug { -public: - EnableGLDebugOperation(); + class EnableGLDebugOperation : public osg::GraphicsOperation + { + public: + EnableGLDebugOperation(); - virtual void operator()(osg::GraphicsContext* graphicsContext); + virtual void operator()(osg::GraphicsContext* graphicsContext); -private: - OpenThreads::Mutex mMutex; -}; + private: + OpenThreads::Mutex mMutex; + }; +} #endif From e147f6fed9d9a2545c2aa2aa935ba0a08f4e7373 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Oct 2018 15:19:31 +0100 Subject: [PATCH 05/50] Finsih gldebug move completely this time --- components/debug/gldebug.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/debug/gldebug.cpp b/components/debug/gldebug.cpp index 52a1bb5a6..2c5b38845 100644 --- a/components/debug/gldebug.cpp +++ b/components/debug/gldebug.cpp @@ -102,11 +102,11 @@ void enableGLDebugExtension(unsigned int contextID) Log(Debug::Error) << "Unable to attach OpenGL debug callback."; } -EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation("EnableGLDebugOperation", false) +Debug::EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation("EnableGLDebugOperation", false) { } -void EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsContext) +void Debug::EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsContext) { OpenThreads::ScopedLock lock(mMutex); From 6de1deeb2d0555480f2fbf56fe56ccc86ca8bc53 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Oct 2018 15:25:12 +0100 Subject: [PATCH 06/50] Include gldebug attribution and licence --- components/debug/gldebug.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/components/debug/gldebug.cpp b/components/debug/gldebug.cpp index 2c5b38845..76e7a4bb9 100644 --- a/components/debug/gldebug.cpp +++ b/components/debug/gldebug.cpp @@ -1,3 +1,34 @@ +// This file is based heavily on code from https://github.com/ThermalPixel/osgdemos/blob/master/osgdebug/EnableGLDebugOperation.cpp +// The original licence is included below: +/* +Copyright (c) 2014, Andreas Klein +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + #include "gldebug.hpp" #include From 0d6be9bd18e8d41221efa3549bce21e672e6108b Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Thu, 6 Aug 2020 00:58:18 +0200 Subject: [PATCH 07/50] More accurate detection of cyclic includes --- components/shader/shadermanager.cpp | 77 ++++++++++++++++------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index be662990b..8523a2962 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -61,45 +61,39 @@ namespace Shader return true; } - bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& templateName) + // Recursively replaces include statements with the actual source of the included files. + // Adjusts #line statements accordingly and detects cyclic includes. + // includingFiles is the set of files that include this file directly or indirectly, and is intentionally not a reference to allow automatic cleanup. + static bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& fileName, int& fileNumber, std::set includingFiles) { + // An include is cyclic if it is being included by itself + if (includingFiles.insert(shaderPath/fileName).second == false) + { + Log(Debug::Error) << "Shader " << fileName << " error: Detected cyclic #includes"; + return false; + } + Misc::StringUtils::replaceAll(source, "\r\n", "\n"); - std::set includedFiles; size_t foundPos = 0; - int fileNumber = 1; while ((foundPos = source.find("#include")) != std::string::npos) { size_t start = source.find('"', foundPos); - if (start == std::string::npos || start == source.size()-1) + if (start == std::string::npos || start == source.size() - 1) { - Log(Debug::Error) << "Shader " << templateName << " error: Invalid #include"; + Log(Debug::Error) << "Shader " << fileName << " error: Invalid #include"; return false; } - size_t end = source.find('"', start+1); + size_t end = source.find('"', start + 1); if (end == std::string::npos) { - Log(Debug::Error) << "Shader " << templateName << " error: Invalid #include"; + Log(Debug::Error) << "Shader " << fileName << " error: Invalid #include"; return false; } - std::string includeFilename = source.substr(start+1, end-(start+1)); + std::string includeFilename = source.substr(start + 1, end - (start + 1)); boost::filesystem::path includePath = shaderPath / includeFilename; - boost::filesystem::ifstream includeFstream; - includeFstream.open(includePath); - if (includeFstream.fail()) - { - Log(Debug::Error) << "Shader " << templateName << " error: Failed to open include " << includePath.string(); - return false; - } - - std::stringstream buffer; - buffer << includeFstream.rdbuf(); - std::string stringRepresentation = buffer.str(); - addLineDirectivesAfterConditionalBlocks(stringRepresentation); - - // insert #line directives so we get correct line numbers in compiler errors - int includedFileNumber = fileNumber++; + // Determine the line number that will be used for the #line directive following the included source size_t lineDirectivePosition = source.rfind("#line", foundPos); int lineNumber; if (lineDirectivePosition != std::string::npos) @@ -116,16 +110,30 @@ namespace Shader } lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\n'); + // Include the file recursively + boost::filesystem::ifstream includeFstream; + includeFstream.open(includePath); + if (includeFstream.fail()) + { + Log(Debug::Error) << "Shader " << fileName << " error: Failed to open include " << includePath.string(); + return false; + } + int includedFileNumber = fileNumber++; + + std::stringstream buffer; + buffer << includeFstream.rdbuf(); + std::string stringRepresentation = buffer.str(); + if (!addLineDirectivesAfterConditionalBlocks(stringRepresentation) + || !parseIncludes(shaderPath, stringRepresentation, includeFilename, fileNumber, includingFiles)) + { + Log(Debug::Error) << "In file included from " << fileName << "." << lineNumber; + return false; + } + std::stringstream toInsert; toInsert << "#line 0 " << includedFileNumber << "\n" << stringRepresentation << "\n#line " << lineNumber << " 0\n"; - source.replace(foundPos, (end-foundPos+1), toInsert.str()); - - if (includedFiles.insert(includePath).second == false) - { - Log(Debug::Error) << "Shader " << templateName << " error: Detected cyclic #includes"; - return false; - } + source.replace(foundPos, (end - foundPos + 1), toInsert.str()); } return true; } @@ -282,21 +290,22 @@ namespace Shader TemplateMap::iterator templateIt = mShaderTemplates.find(templateName); if (templateIt == mShaderTemplates.end()) { - boost::filesystem::path p = (boost::filesystem::path(mPath) / templateName); + boost::filesystem::path path = (boost::filesystem::path(mPath) / templateName); boost::filesystem::ifstream stream; - stream.open(p); + stream.open(path); if (stream.fail()) { - Log(Debug::Error) << "Failed to open " << p.string(); + Log(Debug::Error) << "Failed to open " << path.string(); return nullptr; } std::stringstream buffer; buffer << stream.rdbuf(); // parse includes + int fileNumber = 1; std::string source = buffer.str(); if (!addLineDirectivesAfterConditionalBlocks(source) - || !parseIncludes(boost::filesystem::path(mPath), source, templateName)) + || !parseIncludes(boost::filesystem::path(mPath), source, templateName, fileNumber, {})) return nullptr; templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first; From 297898182b3b448ca2c2286d528ee6336db3ba66 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 28 Aug 2020 15:28:26 +0400 Subject: [PATCH 08/50] Reset attached arrow in the beginning of unequipping animation --- apps/openmw/mwmechanics/character.cpp | 2 ++ apps/openmw/mwrender/animation.hpp | 1 + apps/openmw/mwrender/creatureanimation.cpp | 6 ++++++ apps/openmw/mwrender/creatureanimation.hpp | 1 + apps/openmw/mwrender/npcanimation.cpp | 6 ++++++ apps/openmw/mwrender/npcanimation.hpp | 1 + apps/openmw/mwrender/weaponanimation.cpp | 5 +++++ apps/openmw/mwrender/weaponanimation.hpp | 2 ++ 8 files changed, 24 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5d102f69e..0abf47807 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1306,6 +1306,8 @@ bool CharacterController::updateWeaponState(CharacterState& idle) 1.0f, "unequip start", "unequip stop", 0.0f, 0); mUpperBodyState = UpperCharState_UnEquipingWeap; + mAnimation->detachArrow(); + // If we do not have the "unequip detach" key, hide weapon manually. if (mAnimation->getTextKeyTime(weapgroup+": unequip detach") < 0) mAnimation->showWeapons(false); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index a04a3f999..8a1719db4 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -474,6 +474,7 @@ public: void setAlpha(float alpha); virtual void setPitchFactor(float factor) {} virtual void attachArrow() {} + virtual void detachArrow() {} virtual void releaseArrow(float attackStrength) {} virtual void enableHeadAnimation(bool enable) {} // TODO: move outside of this class diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 489a7a987..4a832c60c 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -208,6 +208,12 @@ bool CreatureWeaponAnimation::isArrowAttached() const return mAmmunition != nullptr; } +void CreatureWeaponAnimation::detachArrow() +{ + WeaponAnimation::detachArrow(mPtr); + updateQuiver(); +} + void CreatureWeaponAnimation::attachArrow() { WeaponAnimation::attachArrow(mPtr); diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index cdcdafe24..071500d74 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -40,6 +40,7 @@ namespace MWRender void updatePart(PartHolderPtr& scene, int slot); virtual void attachArrow(); + virtual void detachArrow(); virtual void releaseArrow(float attackStrength); // WeaponAnimation virtual osg::Group* getArrowBone(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 468938d22..617a0a4ba 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -1051,6 +1051,12 @@ void NpcAnimation::attachArrow() updateQuiver(); } +void NpcAnimation::detachArrow() +{ + WeaponAnimation::detachArrow(mPtr); + updateQuiver(); +} + void NpcAnimation::releaseArrow(float attackStrength) { WeaponAnimation::releaseArrow(mPtr, attackStrength); diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 7edf35a5c..cf695c878 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -142,6 +142,7 @@ public: virtual void showCarriedLeft(bool show); virtual void attachArrow(); + virtual void detachArrow(); virtual void releaseArrow(float attackStrength); virtual osg::Group* getArrowBone(); diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 2af5fdb41..0c2a11466 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -98,6 +98,11 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor) } } +void WeaponAnimation::detachArrow(MWWorld::Ptr actor) +{ + mAmmunition.reset(); +} + void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) { MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); diff --git a/apps/openmw/mwrender/weaponanimation.hpp b/apps/openmw/mwrender/weaponanimation.hpp index a1988703c..dac1b663d 100644 --- a/apps/openmw/mwrender/weaponanimation.hpp +++ b/apps/openmw/mwrender/weaponanimation.hpp @@ -36,6 +36,8 @@ namespace MWRender /// @note If no weapon (or an invalid weapon) is equipped, this function is a no-op. void attachArrow(MWWorld::Ptr actor); + void detachArrow(MWWorld::Ptr actor); + /// @note If no weapon (or an invalid weapon) is equipped, this function is a no-op. void releaseArrow(MWWorld::Ptr actor, float attackStrength); From 47af221f101c1587374cbd6b6ed9f649fd60700e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 28 Aug 2020 15:57:11 +0400 Subject: [PATCH 09/50] Reset ammo when switching view to avoid warnings spam --- apps/openmw/mwrender/npcanimation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 617a0a4ba..31cf3f015 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -363,6 +363,7 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) mViewMode = viewMode; MWBase::Environment::get().getWorld()->scaleObject(mPtr, mPtr.getCellRef().getScale()); // apply race height after view change + mAmmunition.reset(); rebuild(); setRenderBin(); } From 924f634bda9c2b30504a728cd6821334e900ca9b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 8 Jun 2020 15:19:25 +0400 Subject: [PATCH 10/50] Support for multiple summons with same ID in the single spell --- apps/openmw/mwgui/spellicons.cpp | 4 ++-- apps/openmw/mwgui/spellicons.hpp | 4 ++-- apps/openmw/mwmechanics/activespells.cpp | 7 +++--- apps/openmw/mwmechanics/activespells.hpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 26 +++++++++++---------- apps/openmw/mwmechanics/creaturestats.hpp | 2 +- apps/openmw/mwmechanics/linkedeffects.cpp | 1 + apps/openmw/mwmechanics/magiceffects.hpp | 4 ++-- apps/openmw/mwmechanics/spellabsorption.cpp | 4 ++-- apps/openmw/mwmechanics/spellcasting.cpp | 6 +++-- apps/openmw/mwmechanics/spells.cpp | 22 +++++++++++------ apps/openmw/mwmechanics/summoning.cpp | 17 +++++++------- apps/openmw/mwmechanics/summoning.hpp | 6 ++--- apps/openmw/mwworld/inventorystore.cpp | 7 ++++-- apps/openmw/mwworld/inventorystore.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- components/esm/activespells.cpp | 3 +++ components/esm/activespells.hpp | 1 + components/esm/creaturestats.cpp | 13 +++++++---- components/esm/creaturestats.hpp | 2 +- components/esm/savedgame.cpp | 2 +- 21 files changed, 81 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 8a501e598..e6a10ee32 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -25,8 +25,8 @@ namespace MWGui { - void EffectSourceVisitor::visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, + void EffectSourceVisitor::visit (MWMechanics::EffectKey key, int effectIndex, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) { MagicEffectInfo newEffectSource; diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index 26761f2fc..67351688f 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -46,8 +46,8 @@ namespace MWGui virtual ~EffectSourceVisitor() {} - virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, + virtual void visit (MWMechanics::EffectKey key, int effectIndex, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1); }; diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 2ccffcc91..928293e64 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -210,9 +210,8 @@ namespace MWMechanics std::string name = it->second.mDisplayName; float magnitude = effectIt->mMagnitude; - if (magnitude) - visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, effectIt->mTimeLeft, effectIt->mDuration); + visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), effectIt->mEffectIndex, name, it->first, it->second.mCasterActorId, magnitude, effectIt->mTimeLeft, effectIt->mDuration); } } } @@ -258,14 +257,14 @@ namespace MWMechanics mSpellsChanged = true; } - void ActiveSpells::purgeEffect(short effectId, const std::string& sourceId) + void ActiveSpells::purgeEffect(short effectId, const std::string& sourceId, int effectIndex) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { - if (effectIt->mEffectId == effectId && it->first == sourceId) + if (effectIt->mEffectId == effectId && it->first == sourceId && (effectIndex < 0 || effectIndex == effectIt->mEffectIndex)) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 9a1783bc9..4d36c717e 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -85,7 +85,7 @@ namespace MWMechanics void purgeEffect (short effectId); /// Remove all active effects with this effect id and source id - void purgeEffect (short effectId, const std::string& sourceId); + void purgeEffect (short effectId, const std::string& sourceId, int effectIndex=-1); /// Remove all active effects, if roll succeeds (for each effect) void purgeAll(float chance, bool spellOnly = false); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 14a2e17c9..21f71ce5d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -88,12 +88,13 @@ class CheckActorCommanded : public MWMechanics::EffectSourceVisitor MWWorld::Ptr mActor; public: bool mCommanded; + CheckActorCommanded(const MWWorld::Ptr& actor) : mActor(actor) - , mCommanded(false){} + , mCommanded(false){} - virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, + virtual void visit (MWMechanics::EffectKey key, int effectIndex, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { if (((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc()) @@ -156,8 +157,8 @@ namespace MWMechanics GetStuntedMagickaDuration(const MWWorld::Ptr& actor) : mRemainingTime(0.f){} - virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, + virtual void visit (MWMechanics::EffectKey key, int effectIndex, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { if (mRemainingTime == -1) return; @@ -186,8 +187,8 @@ namespace MWMechanics { } - virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, + virtual void visit (MWMechanics::EffectKey key, int effectIndex, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { if (magnitude <= 0) @@ -206,8 +207,8 @@ namespace MWMechanics { public: - virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, + virtual void visit (MWMechanics::EffectKey key, int effectIndex, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { if (key.mId != ESM::MagicEffect::Corprus) @@ -231,8 +232,8 @@ namespace MWMechanics { } - virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, + virtual void visit (MWMechanics::EffectKey key, int effectIndex, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { if (mTrapped) @@ -894,7 +895,7 @@ namespace MWMechanics { } - virtual void visit (MWMechanics::EffectKey key, + virtual void visit (MWMechanics::EffectKey key, int /*effectIndex*/, const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, float magnitude, float remainingTime = -1, float /*totalTime*/ = -1) { @@ -1196,6 +1197,7 @@ namespace MWMechanics { UpdateSummonedCreatures updateSummonedCreatures(ptr); creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures); + creatureStats.getSpells().visitEffectSources(updateSummonedCreatures); if (ptr.getClass().hasInventoryStore(ptr)) ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures); updateSummonedCreatures.process(mTimerDisposeSummonsCorpses == 0.f); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 5e91a1b5a..9a1996ddb 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -84,7 +84,7 @@ namespace MWMechanics float mSideMovementAngle; public: - typedef std::pair SummonKey; // + typedef std::tuple SummonKey; // private: std::map mSummonedCreatures; // diff --git a/apps/openmw/mwmechanics/linkedeffects.cpp b/apps/openmw/mwmechanics/linkedeffects.cpp index 364358433..b0defac7d 100644 --- a/apps/openmw/mwmechanics/linkedeffects.cpp +++ b/apps/openmw/mwmechanics/linkedeffects.cpp @@ -58,6 +58,7 @@ namespace MWMechanics std::vector absorbEffects; ActiveSpells::ActiveEffect absorbEffect = appliedEffect; absorbEffect.mMagnitude *= -1; + absorbEffect.mEffectIndex = appliedEffect.mEffectIndex; absorbEffects.emplace_back(absorbEffect); // Morrowind negates reflected Absorb spells so the original caster won't be harmed. diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 86f5a1804..12735a87f 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -74,8 +74,8 @@ namespace MWMechanics { virtual ~EffectSourceVisitor() { } - virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, + virtual void visit (EffectKey key, int effectIndex, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) = 0; }; diff --git a/apps/openmw/mwmechanics/spellabsorption.cpp b/apps/openmw/mwmechanics/spellabsorption.cpp index f38fd78e2..f22cef3f6 100644 --- a/apps/openmw/mwmechanics/spellabsorption.cpp +++ b/apps/openmw/mwmechanics/spellabsorption.cpp @@ -23,8 +23,8 @@ namespace MWMechanics GetAbsorptionProbability() = default; - virtual void visit (MWMechanics::EffectKey key, - const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, + virtual void visit (MWMechanics::EffectKey key, int /*effectIndex*/, + const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, float magnitude, float /*remainingTime*/, float /*totalTime*/) { if (key.mId == ESM::MagicEffect::SpellAbsorption) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 9f7108239..fb195bb6d 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -121,8 +121,9 @@ namespace MWMechanics // Try absorbing the spell. Some handling must still happen for absorbed effects. bool absorbed = absorbSpell(spell, caster, target); + int currentEffectIndex = 0; for (std::vector::const_iterator effectIt (effects.mList.begin()); - !target.isEmpty() && effectIt != effects.mList.end(); ++effectIt) + !target.isEmpty() && effectIt != effects.mList.end(); ++effectIt, ++currentEffectIndex) { if (effectIt->mRange != range) continue; @@ -189,6 +190,7 @@ namespace MWMechanics effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; effect.mMagnitude = magnitude; effect.mTimeLeft = 0.f; + effect.mEffectIndex = currentEffectIndex; // Avoid applying absorb effects if the caster is the target // We still need the spell to be added @@ -268,7 +270,7 @@ namespace MWMechanics if (isSummoningEffect(effectIt->mEffectID) && !target.isEmpty() && target.getClass().isActor()) { CreatureStats& targetStats = target.getClass().getCreatureStats(target); - std::map::iterator findCreature = targetStats.getSummonedCreatureMap().find(std::make_pair(effectIt->mEffectID, mId)); + std::map::iterator findCreature = targetStats.getSummonedCreatureMap().find(std::make_tuple(effectIt->mEffectID, mId, currentEffectIndex)); if (findCreature != targetStats.getSummonedCreatureMap().end()) { MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(target, findCreature->second); diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index a66c267cc..d292c015d 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -50,7 +50,10 @@ namespace MWMechanics for (const auto& effect : spell->mEffects.mList) { if (iter.second.mPurgedEffects.find(i) != iter.second.mPurgedEffects.end()) + { + ++i; continue; // effect was purged + } float random = 1.f; if (iter.second.mEffectRands.find(i) != iter.second.mEffectRands.end()) @@ -108,7 +111,7 @@ namespace MWMechanics SpellParams params; params.mEffectRands = random; - mSpells.insert (std::make_pair (spell, params)); + mSpells.emplace(spell, params); mSpellsChanged = true; } } @@ -272,7 +275,8 @@ namespace MWMechanics const ESM::Spell * spell = it.first; for (const auto& effectIt : it.second) { - visitor.visit(effectIt.first, spell->mName, spell->mId, -1, effectIt.second.getMagnitude()); + // FIXME: since Spells merges effects with the same ID, there is no sense to use multiple effects with same ID here + visitor.visit(effectIt.first, -1, spell->mName, spell->mId, -1, effectIt.second.getMagnitude()); } } } @@ -308,20 +312,24 @@ namespace MWMechanics void Spells::purgeEffect(int effectId, const std::string & sourceId) { - const ESM::Spell * spell = SpellList::getSpell(sourceId); + // Effect source may be not a spell + const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get().search(sourceId); + if (spell == nullptr) + return; + auto spellIt = mSpells.find(spell); if (spellIt == mSpells.end()) return; - int i = 0; + int index = 0; for (auto& effectIt : spellIt->first->mEffects.mList) { if (effectIt.mEffectID == effectId) { - spellIt->second.mPurgedEffects.insert(i); + spellIt->second.mPurgedEffects.insert(index); mSpellsChanged = true; } - ++i; + ++index; } } @@ -441,7 +449,7 @@ namespace MWMechanics ESM::SpellState::SpellParams params; params.mEffectRands = it.second.mEffectRands; params.mPurgedEffects = it.second.mPurgedEffects; - state.mSpells.insert(std::make_pair(it.first->mId, params)); + state.mSpells.emplace(it.first->mId, params); } } diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 03fd0d681..acb58ea8e 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -65,11 +65,11 @@ namespace MWMechanics { } - void UpdateSummonedCreatures::visit(EffectKey key, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) + void UpdateSummonedCreatures::visit(EffectKey key, int effectIndex, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) { if (isSummoningEffect(key.mId) && magnitude > 0) { - mActiveEffects.insert(std::make_pair(key.mId, sourceId)); + mActiveEffects.insert(std::make_tuple(key.mId, sourceId, effectIndex)); } } @@ -78,12 +78,12 @@ namespace MWMechanics MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); std::map& creatureMap = creatureStats.getSummonedCreatureMap(); - for (std::set >::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it) + for (std::set::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it) { - bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end(); + bool found = creatureMap.find(*it) != creatureMap.end(); if (!found) { - std::string creatureID = getSummonedCreature(it->first); + std::string creatureID = getSummonedCreature(std::get<0>(*it)); if (!creatureID.empty()) { int creatureActorId = -1; @@ -115,7 +115,7 @@ namespace MWMechanics // still insert into creatureMap so we don't try to spawn again every frame, that would spam the warning log } - creatureMap.insert(std::make_pair(*it, creatureActorId)); + creatureMap.emplace(*it, creatureActorId); } } } @@ -149,9 +149,10 @@ namespace MWMechanics if (ptr.isEmpty() || (ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished())) { // Purge the magic effect so a new creature can be summoned if desired - creatureStats.getActiveSpells().purgeEffect(it->first.first, it->first.second); + creatureStats.getActiveSpells().purgeEffect(std::get<0>(it->first), std::get<1>(it->first), std::get<2>(it->first)); + creatureStats.getSpells().purgeEffect(std::get<0>(it->first), std::get<1>(it->first)); if (mActor.getClass().hasInventoryStore(mActor)) - mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second); + mActor.getClass().getInventoryStore(mActor).purgeEffect(std::get<0>(it->first), std::get<1>(it->first), false, std::get<2>(it->first)); MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second); creatureMap.erase(it++); diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp index f24413120..78a1969a9 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -20,8 +20,8 @@ namespace MWMechanics UpdateSummonedCreatures(const MWWorld::Ptr& actor); virtual ~UpdateSummonedCreatures() = default; - virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, + virtual void visit (MWMechanics::EffectKey key, int effectIndex, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1); /// To call after all effect sources have been visited @@ -30,7 +30,7 @@ namespace MWMechanics private: MWWorld::Ptr mActor; - std::set > mActiveEffects; + std::set> mActiveEffects; }; } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index ada211470..d67a22884 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -917,7 +917,7 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito float magnitude = effect.mMagnMin + (effect.mMagnMax - effect.mMagnMin) * params.mRandom; magnitude *= params.mMultiplier; if (magnitude > 0) - visitor.visit(MWMechanics::EffectKey(effect), (**iter).getClass().getName(**iter), (**iter).getCellRef().getRefId(), -1, magnitude); + visitor.visit(MWMechanics::EffectKey(effect), i-1, (**iter).getClass().getName(**iter), (**iter).getCellRef().getRefId(), -1, magnitude); } } } @@ -931,7 +931,7 @@ void MWWorld::InventoryStore::purgeEffect(short effectId, bool wholeSpell) } } -void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId, bool wholeSpell) +void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId, bool wholeSpell, int effectIndex) { TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId); if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end()) @@ -964,6 +964,9 @@ void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sou if (effectIt->mEffectID != effectId) continue; + if (effectIndex >= 0 && effectIndex != i) + continue; + if (wholeSpell) { mPermanentMagicEffectMagnitudes.erase(sourceId); diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index d597e5f30..97ca931e7 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -206,7 +206,7 @@ namespace MWWorld void purgeEffect (short effectId, bool wholeSpell = false); ///< Remove a magic effect - void purgeEffect (short effectId, const std::string& sourceId, bool wholeSpell = false); + void purgeEffect (short effectId, const std::string& sourceId, bool wholeSpell = false, int effectIndex=-1); ///< Remove a magic effect virtual void clear(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index dafd84388..265a7663c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3119,7 +3119,7 @@ namespace MWWorld { } - virtual void visit (MWMechanics::EffectKey key, + virtual void visit (MWMechanics::EffectKey key, int /*effectIndex*/, const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, float /*magnitude*/, float /*remainingTime*/ = -1, float /*totalTime*/ = -1) { diff --git a/components/esm/activespells.cpp b/components/esm/activespells.cpp index 46558ceb7..4017a4933 100644 --- a/components/esm/activespells.cpp +++ b/components/esm/activespells.cpp @@ -24,6 +24,7 @@ namespace ESM esm.writeHNT ("ARG_", effectIt->mArg); esm.writeHNT ("MAGN", effectIt->mMagnitude); esm.writeHNT ("DURA", effectIt->mDuration); + esm.writeHNT ("EIND", effectIt->mEffectIndex); esm.writeHNT ("LEFT", effectIt->mTimeLeft); } } @@ -53,6 +54,8 @@ namespace ESM esm.getHNOT(effect.mArg, "ARG_"); esm.getHNT (effect.mMagnitude, "MAGN"); esm.getHNT (effect.mDuration, "DURA"); + effect.mEffectIndex = -1; + esm.getHNOT (effect.mEffectIndex, "EIND"); if (format < 9) effect.mTimeLeft = effect.mDuration; else diff --git a/components/esm/activespells.hpp b/components/esm/activespells.hpp index 20b2f652d..1b7f8b319 100644 --- a/components/esm/activespells.hpp +++ b/components/esm/activespells.hpp @@ -22,6 +22,7 @@ namespace ESM int mArg; // skill or attribute float mDuration; float mTimeLeft; + int mEffectIndex; }; // format 0, saved games only diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 6f0b36f8d..4ffe567c6 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -115,9 +115,11 @@ void ESM::CreatureStats::load (ESMReader &esm) int magicEffect; esm.getHT(magicEffect); std::string source = esm.getHNOString("SOUR"); + int effectIndex = -1; + esm.getHNOT (effectIndex, "EIND"); int actorId; esm.getHNT (actorId, "ACID"); - mSummonedCreatureMap[std::make_pair(magicEffect, source)] = actorId; + mSummonedCreatureMap[std::make_tuple(magicEffect, source, effectIndex)] = actorId; } while (esm.isNextSub("GRAV")) @@ -212,10 +214,13 @@ void ESM::CreatureStats::save (ESMWriter &esm) const mAiSequence.save(esm); mMagicEffects.save(esm); - for (std::map, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it) + for (std::map, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it) { - esm.writeHNT ("SUMM", it->first.first); - esm.writeHNString ("SOUR", it->first.second); + esm.writeHNT ("SUMM", std::get<0>(it->first)); + esm.writeHNString ("SOUR", std::get<1>(it->first)); + int effectIndex = std::get<2>(it->first); + if (effectIndex != -1) + esm.writeHNT ("EIND", effectIndex); esm.writeHNT ("ACID", it->second); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 79a576587..e79d430ce 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -39,7 +39,7 @@ namespace ESM bool mHasAiSettings; StatState mAiSettings[4]; - std::map, int> mSummonedCreatureMap; + std::map, int> mSummonedCreatureMap; std::vector mSummonGraveyard; ESM::TimeStamp mTradeTime; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 4b0529703..0fc84e309 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -4,7 +4,7 @@ #include "esmwriter.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 13; +int ESM::SavedGame::sCurrentFormat = 14; void ESM::SavedGame::load (ESMReader &esm) { From 67eace10281f48556d18b389dfcddab1c6c9e173 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 28 Aug 2020 22:43:22 +0400 Subject: [PATCH 11/50] Use struct instead of tuple --- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 7 +++-- apps/openmw/mwmechanics/spellcasting.cpp | 3 ++- apps/openmw/mwmechanics/summoning.cpp | 19 +++++++------- apps/openmw/mwmechanics/summoning.hpp | 4 ++- components/esm/creaturestats.cpp | 22 ++++++++-------- components/esm/creaturestats.hpp | 2 +- components/esm/magiceffects.hpp | 32 +++++++++++++++++++++++ 10 files changed, 65 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 444ce4cb1..a68fddca1 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -252,7 +252,7 @@ namespace MWGui } // Clean up summoned creatures as well - std::map& creatureMap = creatureStats.getSummonedCreatureMap(); + std::map& creatureMap = creatureStats.getSummonedCreatureMap(); for (const auto& creature : creatureMap) MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mPtr, creature.second); creatureMap.clear(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 21f71ce5d..271b352ea 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -2016,7 +2016,7 @@ namespace MWMechanics // Remove the summoned creature's summoned creatures as well MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); - std::map& creatureMap = stats.getSummonedCreatureMap(); + std::map& creatureMap = stats.getSummonedCreatureMap(); for (const auto& creature : creatureMap) cleanupSummonedCreature(stats, creature.second); creatureMap.clear(); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 79b8e23de..1d5fe8347 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -683,7 +683,7 @@ namespace MWMechanics return mTimeOfDeath; } - std::map& CreatureStats::getSummonedCreatureMap() + std::map& CreatureStats::getSummonedCreatureMap() { return mSummonedCreatures; } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 9a1996ddb..b2c0aec98 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -13,6 +13,7 @@ #include "drawstate.hpp" #include +#include namespace ESM { @@ -83,10 +84,8 @@ namespace MWMechanics // The difference between view direction and lower body direction. float mSideMovementAngle; - public: - typedef std::tuple SummonKey; // private: - std::map mSummonedCreatures; // + std::map mSummonedCreatures; // // Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet. // This may be necessary when the creature is in an inactive cell. @@ -235,7 +234,7 @@ namespace MWMechanics void setBlock(bool value); bool getBlock() const; - std::map& getSummonedCreatureMap(); // + std::map& getSummonedCreatureMap(); // std::vector& getSummonedCreatureGraveyard(); // ActorIds enum Flag diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index fb195bb6d..3767acb3f 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -270,7 +270,8 @@ namespace MWMechanics if (isSummoningEffect(effectIt->mEffectID) && !target.isEmpty() && target.getClass().isActor()) { CreatureStats& targetStats = target.getClass().getCreatureStats(target); - std::map::iterator findCreature = targetStats.getSummonedCreatureMap().find(std::make_tuple(effectIt->mEffectID, mId, currentEffectIndex)); + ESM::SummonKey key(effectIt->mEffectID, mId, currentEffectIndex); + auto findCreature = targetStats.getSummonedCreatureMap().find(key); if (findCreature != targetStats.getSummonedCreatureMap().end()) { MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(target, findCreature->second); diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index acb58ea8e..0f699ccad 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -69,21 +69,21 @@ namespace MWMechanics { if (isSummoningEffect(key.mId) && magnitude > 0) { - mActiveEffects.insert(std::make_tuple(key.mId, sourceId, effectIndex)); + mActiveEffects.insert(ESM::SummonKey(key.mId, sourceId, effectIndex)); } } void UpdateSummonedCreatures::process(bool cleanup) { MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); - std::map& creatureMap = creatureStats.getSummonedCreatureMap(); + std::map& creatureMap = creatureStats.getSummonedCreatureMap(); - for (std::set::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it) + for (std::set::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it) { bool found = creatureMap.find(*it) != creatureMap.end(); if (!found) { - std::string creatureID = getSummonedCreature(std::get<0>(*it)); + std::string creatureID = getSummonedCreature(it->mEffectId); if (!creatureID.empty()) { int creatureActorId = -1; @@ -121,7 +121,7 @@ namespace MWMechanics } // Update summon effects - for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) + for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) { bool found = mActiveEffects.find(it->first) != mActiveEffects.end(); if (!found) @@ -143,16 +143,17 @@ namespace MWMechanics if (!cleanup) return; - for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) + for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); if (ptr.isEmpty() || (ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished())) { // Purge the magic effect so a new creature can be summoned if desired - creatureStats.getActiveSpells().purgeEffect(std::get<0>(it->first), std::get<1>(it->first), std::get<2>(it->first)); - creatureStats.getSpells().purgeEffect(std::get<0>(it->first), std::get<1>(it->first)); + const ESM::SummonKey& key = it->first; + creatureStats.getActiveSpells().purgeEffect(key.mEffectId, key.mSourceId, key.mEffectIndex); + creatureStats.getSpells().purgeEffect(key.mEffectId, key.mSourceId); if (mActor.getClass().hasInventoryStore(mActor)) - mActor.getClass().getInventoryStore(mActor).purgeEffect(std::get<0>(it->first), std::get<1>(it->first), false, std::get<2>(it->first)); + mActor.getClass().getInventoryStore(mActor).purgeEffect(key.mEffectId, key.mSourceId, false, key.mEffectIndex); MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second); creatureMap.erase(it++); diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp index 78a1969a9..ac820e32f 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -5,6 +5,8 @@ #include "../mwworld/ptr.hpp" +#include + #include "magiceffects.hpp" namespace MWMechanics @@ -30,7 +32,7 @@ namespace MWMechanics private: MWWorld::Ptr mActor; - std::set> mActiveEffects; + std::set mActiveEffects; }; } diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 4ffe567c6..cb383992c 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -119,7 +119,7 @@ void ESM::CreatureStats::load (ESMReader &esm) esm.getHNOT (effectIndex, "EIND"); int actorId; esm.getHNT (actorId, "ACID"); - mSummonedCreatureMap[std::make_tuple(magicEffect, source, effectIndex)] = actorId; + mSummonedCreatureMap[SummonKey(magicEffect, source, effectIndex)] = actorId; } while (esm.isNextSub("GRAV")) @@ -214,19 +214,19 @@ void ESM::CreatureStats::save (ESMWriter &esm) const mAiSequence.save(esm); mMagicEffects.save(esm); - for (std::map, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it) + for (const auto& summon : mSummonedCreatureMap) { - esm.writeHNT ("SUMM", std::get<0>(it->first)); - esm.writeHNString ("SOUR", std::get<1>(it->first)); - int effectIndex = std::get<2>(it->first); + esm.writeHNT ("SUMM", summon.first.mEffectId); + esm.writeHNString ("SOUR", summon.first.mSourceId); + int effectIndex = summon.first.mEffectIndex; if (effectIndex != -1) esm.writeHNT ("EIND", effectIndex); - esm.writeHNT ("ACID", it->second); + esm.writeHNT ("ACID", summon.second); } - for (std::vector::const_iterator it = mSummonGraveyard.begin(); it != mSummonGraveyard.end(); ++it) + for (int key : mSummonGraveyard) { - esm.writeHNT ("GRAV", *it); + esm.writeHNT ("GRAV", key); } esm.writeHNT("AISE", mHasAiSettings); @@ -236,11 +236,11 @@ void ESM::CreatureStats::save (ESMWriter &esm) const mAiSettings[i].save(esm); } - for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) + for (const auto& corprusSpell : mCorprusSpells) { - esm.writeHNString("CORP", it->first); + esm.writeHNString("CORP", corprusSpell.first); - const CorprusStats & stats = it->second; + const CorprusStats & stats = corprusSpell.second; esm.writeHNT("WORS", stats.mWorsenings); esm.writeHNT("TIME", stats.mNextWorsening); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index e79d430ce..13bc50008 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -39,7 +39,7 @@ namespace ESM bool mHasAiSettings; StatState mAiSettings[4]; - std::map, int> mSummonedCreatureMap; + std::map mSummonedCreatureMap; std::vector mSummonGraveyard; ESM::TimeStamp mTradeTime; diff --git a/components/esm/magiceffects.hpp b/components/esm/magiceffects.hpp index 2a6052caa..94ae23a10 100644 --- a/components/esm/magiceffects.hpp +++ b/components/esm/magiceffects.hpp @@ -2,6 +2,7 @@ #define COMPONENTS_ESM_MAGICEFFECTS_H #include +#include namespace ESM { @@ -18,6 +19,37 @@ namespace ESM void save (ESMWriter &esm) const; }; + struct SummonKey + { + SummonKey(int effectId, const std::string& sourceId, int index) + { + mEffectId = effectId; + mSourceId = sourceId; + mEffectIndex = index; + } + + bool operator==(const SummonKey &other) const + { + return mEffectId == other.mEffectId && + mSourceId == other.mSourceId && + mEffectIndex == other.mEffectIndex; + } + + bool operator<(const SummonKey &other) const + { + if (mEffectId < other.mEffectId) + return true; + + if (mSourceId < other.mSourceId) + return true; + + return mEffectIndex < other.mEffectIndex; + } + + int mEffectId; + std::string mSourceId; + int mEffectIndex; + }; } #endif From 131bd5c91deb7172ecd0ebb893845956cef502fd Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 30 Aug 2020 22:10:49 +0200 Subject: [PATCH 12/50] Fix unused template argument --- apps/openmw/mwmechanics/actorutil.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actorutil.hpp b/apps/openmw/mwmechanics/actorutil.hpp index 275a3a814..490dc119a 100644 --- a/apps/openmw/mwmechanics/actorutil.hpp +++ b/apps/openmw/mwmechanics/actorutil.hpp @@ -58,7 +58,7 @@ namespace MWMechanics template void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) { - ESM::NPC copy = *MWBase::Environment::get().getWorld()->getStore().get().find(actorId); + T copy = *MWBase::Environment::get().getWorld()->getStore().get().find(actorId); for(auto& it : copy.mInventory.mList) { if(Misc::StringUtils::ciEqual(it.mItem, itemId)) From 7e3d19196d722e1bf150365c0af8fe8b7f47498d Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Tue, 1 Sep 2020 00:37:37 +0200 Subject: [PATCH 13/50] Fix #5586 --- apps/openmw/mwclass/npc.cpp | 2 ++ apps/openmw/mwmechanics/character.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b89d79fc9..4aab95f9d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -938,6 +938,8 @@ namespace MWClass float Npc::getMaxSpeed(const MWWorld::Ptr& ptr) const { + // TODO: This function is called several times per frame for each NPC. + // It would be better to calculate it only once per frame for each NPC and save the result in CreatureStats. const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) return 0.f; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5d102f69e..32f8f6f2a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1965,8 +1965,11 @@ void CharacterController::update(float duration, bool animationOnly) vec.normalize(); float effectiveRotation = rot.z(); + bool canMove = cls.getMaxSpeed(mPtr) > 0; static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game"); - if (turnToMovementDirection && !isFirstPersonPlayer) + if (!turnToMovementDirection || isFirstPersonPlayer) + movementSettings.mIsStrafing = std::abs(vec.x()) > std::abs(vec.y()) * 2; + else if (canMove) { float targetMovementAngle = vec.y() >= 0 ? std::atan2(-vec.x(), vec.y()) : std::atan2(vec.x(), -vec.y()); movementSettings.mIsStrafing = (stats.getDrawState() != MWMechanics::DrawState_Nothing || inwater) @@ -1986,8 +1989,6 @@ void CharacterController::update(float duration, bool animationOnly) stats.setSideMovementAngle(stats.getSideMovementAngle() + delta); effectiveRotation += delta; } - else - movementSettings.mIsStrafing = std::abs(vec.x()) > std::abs(vec.y()) * 2; mAnimation->setLegsYawRadians(stats.getSideMovementAngle()); if (stats.getDrawState() == MWMechanics::DrawState_Nothing || inwater) From 7a4efe3979c687cc36c783382349731eef7aa1d4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 1 Sep 2020 10:06:31 +0400 Subject: [PATCH 14/50] Avoid crash when object paging encounters an empty shape --- apps/openmw/mwrender/objectpaging.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 0f7e1c422..c756a3fc7 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -289,7 +289,9 @@ namespace MWRender } virtual void apply(osg::Geometry& geom) { - mResult.mNumVerts += geom.getVertexArray()->getNumElements(); + if (osg::Array* array = geom.getVertexArray()) + mResult.mNumVerts += array->getNumElements(); + ++mResult.mStateSetCounter[mCurrentStateSet]; ++mGlobalStateSetCounter[mCurrentStateSet]; } From 787ca06d558b6327b8cb8eb2f7dde1af57dc9dec Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 1 Sep 2020 19:36:59 +0400 Subject: [PATCH 15/50] Apply effects during rest before fast-forwarding spells state --- apps/openmw/mwmechanics/actors.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 271b352ea..0dfa7a9e6 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -2053,10 +2053,11 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { - iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); - if (iter->first.getClass().getCreatureStats(iter->first).isDead()) + { + iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); continue; + } if (!sleep || iter->first == player) restoreDynamicStats(iter->first, hours, sleep); @@ -2073,13 +2074,14 @@ namespace MWMechanics if (iter->first.getClass().isNpc()) calculateNpcStatModifiers(iter->first, duration); + iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); + MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first); if (animation) { animation->removeEffects(); MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first); } - } fastForwardAi(); From f9ca08a9843b4666180dececa4506659f250e375 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 3 Sep 2020 09:11:22 +0400 Subject: [PATCH 16/50] Do not use swish sound for ranged weapons --- apps/openmw/mwmechanics/character.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 32f8f6f2a..8087b499e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1788,7 +1788,9 @@ bool CharacterController::updateWeaponState(CharacterState& idle) mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; } - playSwishSound(0.0f); + + if(weapclass != ESM::WeaponType::Ranged && weapclass != ESM::WeaponType::Thrown) + playSwishSound(0.0f); } if(mAttackType == "shoot") From a251461045baf6115a56621c23da250dbcad75be Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 3 Sep 2020 13:50:24 +0000 Subject: [PATCH 17/50] Unpin CMake version now latest is fixed --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b89b99ac8..d171e8222 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -60,7 +60,7 @@ variables: &cs-targets - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolately/" --priority=1 - choco install git --force --params "/GitAndUnixToolsOnPath" -y - choco install 7zip -y - - choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' --version=3.18.0 -y + - choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y - choco install vswhere -y - choco install ninja -y - choco install python -y From 257a6b462937e1173ccd052ca792314243c3d96d Mon Sep 17 00:00:00 2001 From: Eli2 Date: Fri, 31 Jan 2020 23:58:21 +0100 Subject: [PATCH 18/50] Use cmake method to enable interprocedural optimizations --- CMakeLists.txt | 32 ++++++++++++++++++-------- extern/recastnavigation/CMakeLists.txt | 5 ++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6e7e4ef3..5496b3145 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,11 @@ project(OpenMW) cmake_minimum_required(VERSION 3.1.0) +# for link time optimization, remove if cmake version is >= 3.9 +if(POLICY CMP0069) + cmake_policy(SET CMP0069 NEW) +endif() + # Apps and tools option(BUILD_OPENMW "Build OpenMW" ON) option(BUILD_LAUNCHER "Build Launcher" ON) @@ -97,6 +102,7 @@ option(OSG_STATIC "Link static build of OpenSceneGraph into the binaries" FALSE) option(QT_STATIC "Link static build of QT into the binaries" FALSE) option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE) +option(OPENMW_LTO_BUILD "Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)" OFF) # what is necessary to build documentation IF( BUILD_DOCS ) @@ -110,7 +116,6 @@ option(OPENMW_OSX_DEPLOYMENT OFF) if (MSVC) option(OPENMW_MP_BUILD "Build OpenMW with /MP flag" OFF) - option(OPENMW_LTO_BUILD "Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)" OFF) endif() # Set up common paths @@ -388,6 +393,23 @@ endif() # CXX Compiler settings set(CMAKE_CXX_STANDARD 14) + +if(OPENMW_LTO_BUILD) + if(NOT CMAKE_VERSION VERSION_LESS 3.9) + include(CheckIPOSupported) + check_ipo_supported(RESULT HAVE_IPO OUTPUT HAVE_IPO_OUTPUT) + if(HAVE_IPO) + message(STATUS "LTO enabled") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(WARNING "Requested option OPENMW_LTO_BUILD not supported by this compiler: ${HAVE_IPO_OUTPUT}") + endif() + else() + message(WARNING "Requested option OPENMW_LTO_BUILD not supported by this cmake version: ${CMAKE_VERSION}") + endif() +endif() + + if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++14 -pedantic -Wno-long-long") add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON ) @@ -407,14 +429,6 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter") endif() elseif (MSVC) - # Enable link-time code generation globally for all linking - if (OPENMW_LTO_BUILD) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") - set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG") - set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} /LTCG") - endif() - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE") endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) diff --git a/extern/recastnavigation/CMakeLists.txt b/extern/recastnavigation/CMakeLists.txt index 4952e51da..0d31c2e36 100644 --- a/extern/recastnavigation/CMakeLists.txt +++ b/extern/recastnavigation/CMakeLists.txt @@ -1,5 +1,10 @@ cmake_minimum_required(VERSION 3.0) +# for link time optimization, remove if cmake version is >= 3.9 +if(POLICY CMP0069) + cmake_policy(SET CMP0069 NEW) +endif() + project(RecastNavigation) # lib versions From 1dcea961c63442fa518759328b55c6b88b4cfa7c Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 3 Sep 2020 20:15:53 +0100 Subject: [PATCH 19/50] Only enable LTO for release This is how it was for the original implementation --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5496b3145..8822301cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -399,8 +399,8 @@ if(OPENMW_LTO_BUILD) include(CheckIPOSupported) check_ipo_supported(RESULT HAVE_IPO OUTPUT HAVE_IPO_OUTPUT) if(HAVE_IPO) - message(STATUS "LTO enabled") - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + message(STATUS "LTO enabled for Release configuration.") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) else() message(WARNING "Requested option OPENMW_LTO_BUILD not supported by this compiler: ${HAVE_IPO_OUTPUT}") endif() From 643db61dfb7a4054e50de206c74bb121a88f2048 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 3 Sep 2020 20:38:02 +0100 Subject: [PATCH 20/50] Make warnings more informative. --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8822301cf..444743d28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -403,9 +403,12 @@ if(OPENMW_LTO_BUILD) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) else() message(WARNING "Requested option OPENMW_LTO_BUILD not supported by this compiler: ${HAVE_IPO_OUTPUT}") + if(MSVC) + message(STATUS "Note: Flags used to be set manually for this setting with MSVC. We now rely on CMake for this. Upgrade CMake to at least 3.13 to re-enable this setting.") + endif() endif() else() - message(WARNING "Requested option OPENMW_LTO_BUILD not supported by this cmake version: ${CMAKE_VERSION}") + message(WARNING "Requested option OPENMW_LTO_BUILD not supported by this cmake version: ${CMAKE_VERSION}. Upgrade CMake to at least 3.9 to enable support for certain compilers. Newer CMake versions support more compilers.") endif() endif() From f4db29a717b2a05049a65bef02a9fdf5fafb5081 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 4 Sep 2020 00:45:41 +0100 Subject: [PATCH 21/50] Kill BUILD_CONFIG It was basically just CONFIGURATION but less confusing. CONFIGURATION could just be less confusing. --- CI/before_script.msvc.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 616caf3fa..7698dc877 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -380,17 +380,14 @@ esac case $CONFIGURATION in debug|Debug|DEBUG ) CONFIGURATION=Debug - BUILD_CONFIG=Debug ;; release|Release|RELEASE ) CONFIGURATION=Release - BUILD_CONFIG=Release ;; relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO ) - CONFIGURATION=Release - BUILD_CONFIG=RelWithDebInfo + CONFIGURATION=RelWithDebInfo ;; esac @@ -422,7 +419,7 @@ else fi if [ -n "$SINGLE_CONFIG" ]; then - add_cmake_opts "-DCMAKE_BUILD_TYPE=${BUILD_CONFIG}" + add_cmake_opts "-DCMAKE_BUILD_TYPE=${CONFIGURATION}" fi if ! [ -z $UNITY_BUILD ]; then @@ -525,7 +522,7 @@ elif [ -n "$NINJA" ]; then fi if [ -n "$SINGLE_CONFIG" ]; then - BUILD_DIR="${BUILD_DIR}_${BUILD_CONFIG}" + BUILD_DIR="${BUILD_DIR}_${CONFIGURATION}" fi if [ -z $KEEP ]; then @@ -907,8 +904,8 @@ fi echo "- Copying Runtime DLLs..." DLL_PREFIX="" if [ -z $SINGLE_CONFIG ]; then - mkdir -p $BUILD_CONFIG - DLL_PREFIX="$BUILD_CONFIG/" + mkdir -p $CONFIGURATION + DLL_PREFIX="$CONFIGURATION/" fi for DLL in $RUNTIME_DLLS; do TARGET="$(basename "$DLL")" From 9f1fbd56a3aae38a683f44546bccf252ca575b28 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 4 Sep 2020 13:45:38 +0400 Subject: [PATCH 22/50] Fix SummonKey comparison function --- components/esm/magiceffects.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/esm/magiceffects.hpp b/components/esm/magiceffects.hpp index 94ae23a10..a931c68fa 100644 --- a/components/esm/magiceffects.hpp +++ b/components/esm/magiceffects.hpp @@ -39,9 +39,13 @@ namespace ESM { if (mEffectId < other.mEffectId) return true; + if (mEffectId > other.mEffectId) + return false; if (mSourceId < other.mSourceId) return true; + if (mSourceId > other.mSourceId) + return false; return mEffectIndex < other.mEffectIndex; } From 2d021430b87c28fbc947e3068d8e4708cc7bbb41 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 5 Sep 2020 11:52:52 +0000 Subject: [PATCH 23/50] force static data variance for water geometry --- apps/openmw/mwrender/water.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index c9d16b728..a1691d5d1 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -443,6 +443,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900); mWaterGeom->setDrawCallback(new DepthClampCallback); mWaterGeom->setNodeMask(Mask_Water); + mWaterGeom->setDataVariance(osg::Object::STATIC); mWaterNode = new osg::PositionAttitudeTransform; mWaterNode->setName("Water Root"); From 9e547e14d2bb9ba982764e9dca4144b6952cb1ef Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 8 Sep 2020 00:18:18 +0100 Subject: [PATCH 24/50] Allow setting up multiple build configurations at once Also fix some bugs discovered in the process. For multi-config generators, this basically just copies the DLLs for each configuration, and for single-config, due to there being separate build directories with separate extracted dependencies for each, it defaults to just one, and will run the script several times if you manually specify several. Details include: * Changing CONFIGURATION from a string to an array called CONFIGURATIONS. This gets iterated over in a bunch of places. * Fixing a typo of 'cannot' * Making the DLL lists arrays per-config, too. * Some handling for the recursive stuff and a warning if configurations are set with a multi-config generator. * Moving the configuration name sanitisation after they've been set. * Myriad changes to Google Test: - Build it in a directory specific to the build tools - previously, having an MSVC 2017 and MSVC 2019 build on the same machine was impossible if unit tests were on, even though it's allowed otherwise - Use either Debug or Release Google Test as its finder isn't looking for RelWithDebInfo or capable of dealing with it if we try and use it anyway. - Always build Google Test with MSBuild as it's much less hassle due to CMake setting up the environment for us. Currently, MSVC always comes with something that can build solution files, no matter how you get it, so this shouldn't upset anyone. - Use CMake's --install mode so we can set the install prefix in the place that uses it. - Pass CMake both Debug and Release Google Test instead of risking a C/C++ library configuration mismatch causing linker and runtime errors - it'll pick a suitable one for each configuration. - Pass the library type explicitly as CMake can't cope without a Release library if you only gave it Debug, due to accessing a Release-specific variable unconditionally. * Remove the -legacy flag from vswhere as it's only needed for MSVC 2015, which we don't support any more. * Fix the -version argument for vswhere as I'd massively cocked it up. I don't know how that happened as I did test it on a machine with multiple MSVC versions installed, which was the failure case, but it didn't fail then. --- CI/before_script.msvc.sh | 332 +++++++++++++++++++++++++-------------- 1 file changed, 215 insertions(+), 117 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 7698dc877..e43f5623f 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -69,7 +69,7 @@ NMAKE="" NINJA="" PDBS="" PLATFORM="" -CONFIGURATION="" +CONFIGURATIONS=() TEST_FRAMEWORK="" GOOGLE_INSTALL_ROOT="" INSTALL_PREFIX="." @@ -129,7 +129,7 @@ while [ $# -gt 0 ]; do PDBS=true ;; c ) - CONFIGURATION=$1 + CONFIGURATIONS+=( $1 ) shift ;; t ) @@ -143,8 +143,10 @@ while [ $# -gt 0 ]; do cat < + -c Set the configuration, can also be set with environment variable CONFIGURATION. + For mutli-config generators, this is ignored, and all configurations are set up. + For single-config generators, several configurations can be set up at once by specifying -c multiple times. -d Skip checking the downloads. -D @@ -164,7 +166,7 @@ Options: -v <2017/2019> Choose the Visual Studio version to use. -n - Produce NMake makefiles instead of a Visual Studio solution. Cannout be used with -N. + Produce NMake makefiles instead of a Visual Studio solution. Cannot be used with -N. -N Produce Ninja (multi-config if CMake is new enough to support it) files instead of a Visual Studio solution. Cannot be used with -n.. -P @@ -187,7 +189,7 @@ done if [ -n "$NMAKE" ] || [ -n "$NINJA" ]; then if [ -n "$NMAKE" ] && [ -n "$NINJA" ]; then - echo "Cannout run in NMake and Ninja mode at the same time." + echo "Cannot run in NMake and Ninja mode at the same time." wrappedExit 1 fi ACTIVATE_MSVC=true @@ -293,29 +295,40 @@ add_cmake_opts() { CMAKE_OPTS="$CMAKE_OPTS $@" } -RUNTIME_DLLS="" +declare -A RUNTIME_DLLS +RUNTIME_DLLS["Release"]="" +RUNTIME_DLLS["Debug"]="" +RUNTIME_DLLS["RelWithDebInfo"]="" add_runtime_dlls() { - RUNTIME_DLLS="$RUNTIME_DLLS $@" + local CONFIG=$1 + shift + RUNTIME_DLLS[$CONFIG]="${RUNTIME_DLLS[$CONFIG]} $@" } -OSG_PLUGINS="" +declare -A OSG_PLUGINS +OSG_PLUGINS["Release"]="" +OSG_PLUGINS["Debug"]="" +OSG_PLUGINS["RelWithDebInfo"]="" add_osg_dlls() { - OSG_PLUGINS="$OSG_PLUGINS $@" + local CONFIG=$1 + shift + OSG_PLUGINS[$CONFIG]="${OSG_PLUGINS[$CONFIG]} $@" } -QT_PLATFORMS="" +declare -A QT_PLATFORMS +QT_PLATFORMS["Release"]="" +QT_PLATFORMS["Debug"]="" +QT_PLATFORMS["RelWithDebInfo"]="" add_qt_platform_dlls() { - QT_PLATFORMS="$QT_PLATFORMS $@" + local CONFIG=$1 + shift + QT_PLATFORMS[$CONFIG]="${QT_PLATFORMS[$CONFIG]} $@" } if [ -z $PLATFORM ]; then PLATFORM="$(uname -m)" fi -if [ -z $CONFIGURATION ]; then - CONFIGURATION="Debug" -fi - if [ -z $VS_VERSION ]; then VS_VERSION="2017" fi @@ -377,20 +390,6 @@ case $PLATFORM in ;; esac -case $CONFIGURATION in - debug|Debug|DEBUG ) - CONFIGURATION=Debug - ;; - - release|Release|RELEASE ) - CONFIGURATION=Release - ;; - - relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO ) - CONFIGURATION=RelWithDebInfo - ;; -esac - if [ $BITS -eq 64 ] && [ $MSVC_REAL_VER -lt 16 ]; then GENERATOR="${GENERATOR} Win64" fi @@ -408,6 +407,79 @@ if [ -n "$NINJA" ]; then fi fi +if [ -n "$SINGLE_CONFIG" ]; then + if [ ${#CONFIGURATIONS[@]} -eq 0 ]; then + if [ -n "${CONFIGURATION:-}" ]; then + CONFIGURATIONS=("$CONFIGURATION") + else + CONFIGURATIONS=("Debug") + fi + elif [ ${#CONFIGURATIONS[@]} -ne 1 ]; then + # It's simplest just to recursively call the script a few times. + RECURSIVE_OPTIONS=() + if [ -n "$VERBOSE" ]; then + RECURSIVE_OPTIONS+=("-V") + fi + if [ -n "$SKIP_DOWNLOAD" ]; then + RECURSIVE_OPTIONS+=("-d") + fi + if [ -n "$BULLET_DOUBLE" ]; then + RECURSIVE_OPTIONS+=("-D") + fi + if [ -n "$SKIP_EXTRACT" ]; then + RECURSIVE_OPTIONS+=("-e") + fi + if [ -n "$KEEP" ]; then + RECURSIVE_OPTIONS+=("-k") + fi + if [ -n "$UNITY_BUILD" ]; then + RECURSIVE_OPTIONS+=("-u") + fi + if [ -n "$NMAKE" ]; then + RECURSIVE_OPTIONS+=("-n") + fi + if [ -n "$NINJA" ]; then + RECURSIVE_OPTIONS+=("-N") + fi + if [ -n "$PDBS" ]; then + RECURSIVE_OPTIONS+=("-P") + fi + if [ -n "$TEST_FRAMEWORK" ]; then + RECURSIVE_OPTIONS+=("-t") + fi + RECURSIVE_OPTIONS+=("-v $VS_VERSION") + RECURSIVE_OPTIONS+=("-p $PLATFORM") + RECURSIVE_OPTIONS+=("-i '$INSTALL_PREFIX'") + + for config in ${CONFIGURATIONS[@]}; do + $0 ${RECURSIVE_OPTIONS[@]} -c $config + done + + wrappedExit 1 + fi +else + if [ ${#CONFIGURATIONS[@]} -ne 0 ]; then + echo "Ignoring configurations argument - generator is multi-config" + fi + CONFIGURATIONS=("Release" "Debug" "RelWithDebInfo") +fi + +for i in ${!CONFIGURATIONS[@]}; do + case ${CONFIGURATIONS[$i]} in + debug|Debug|DEBUG ) + CONFIGURATIONS[$i]=Debug + ;; + + release|Release|RELEASE ) + CONFIGURATIONS[$i]=Release + ;; + + relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO ) + CONFIGURATIONS[$i]=RelWithDebInfo + ;; + esac +done + if [ $MSVC_REAL_VER -ge 16 ] && [ -z "$NMAKE" ] && [ -z "$NINJA" ]; then if [ $BITS -eq 64 ]; then add_cmake_opts "-G\"$GENERATOR\" -A x64" @@ -419,7 +491,7 @@ else fi if [ -n "$SINGLE_CONFIG" ]; then - add_cmake_opts "-DCMAKE_BUILD_TYPE=${CONFIGURATION}" + add_cmake_opts "-DCMAKE_BUILD_TYPE=${CONFIGURATIONS[0]}" fi if ! [ -z $UNITY_BUILD ]; then @@ -522,7 +594,7 @@ elif [ -n "$NINJA" ]; then fi if [ -n "$SINGLE_CONFIG" ]; then - BUILD_DIR="${BUILD_DIR}_${CONFIGURATION}" + BUILD_DIR="${BUILD_DIR}_${CONFIGURATIONS[0]}" fi if [ -z $KEEP ]; then @@ -623,7 +695,9 @@ printf "FFmpeg 4.2.2... " rm -rf "ffmpeg-4.2.2-win${BITS}-dev" fi export FFMPEG_HOME="$(real_pwd)/FFmpeg" - add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-58,avformat-58,avutil-56,swresample-3,swscale-5}.dll + for config in ${CONFIGURATIONS[@]}; do + add_runtime_dlls $config "$(pwd)/FFmpeg/bin/"{avcodec-58,avformat-58,avutil-56,swresample-3,swscale-5}.dll + done if [ $BITS -eq 32 ]; then add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\"" fi @@ -648,14 +722,16 @@ printf "MyGUI 3.4.0... " mv "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}" MyGUI fi export MYGUI_HOME="$(real_pwd)/MyGUI" - if [ $CONFIGURATION == "Debug" ]; then - SUFFIX="_d" - MYGUI_CONFIGURATION="Debug" - else - SUFFIX="" - MYGUI_CONFIGURATION="RelWithDebInfo" - fi - add_runtime_dlls "$(pwd)/MyGUI/bin/${MYGUI_CONFIGURATION}/MyGUIEngine${SUFFIX}.dll" + for CONFIGURATION in ${CONFIGURATIONS[@]}; do + if [ $CONFIGURATION == "Debug" ]; then + SUFFIX="_d" + MYGUI_CONFIGURATION="Debug" + else + SUFFIX="" + MYGUI_CONFIGURATION="RelWithDebInfo" + fi + add_runtime_dlls $CONFIGURATION "$(pwd)/MyGUI/bin/${MYGUI_CONFIGURATION}/MyGUIEngine${SUFFIX}.dll" + done echo Done. } cd $DEPS @@ -672,7 +748,9 @@ printf "OpenAL-Soft 1.20.1... " OPENAL_SDK="$(real_pwd)/openal-soft-1.20.1-bin" add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \ -DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib" - add_runtime_dlls "$(pwd)/openal-soft-1.20.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" + for config in ${CONFIGURATIONS[@]}; do + add_runtime_dlls $config "$(pwd)/openal-soft-1.20.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" + done echo Done. } cd $DEPS @@ -695,15 +773,17 @@ printf "OSG 3.6.5... " fi OSG_SDK="$(real_pwd)/OSG" add_cmake_opts -DOSG_DIR="$OSG_SDK" - if [ $CONFIGURATION == "Debug" ]; then - SUFFIX="d" - else - SUFFIX="" - fi - add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \ - "$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll - add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll - add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll + for CONFIGURATION in ${CONFIGURATIONS[@]}; do + if [ $CONFIGURATION == "Debug" ]; then + SUFFIX="d" + else + SUFFIX="" + fi + add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \ + "$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll + add_osg_dlls $CONFIGURATION "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll + add_osg_dlls $CONFIGURATION "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll + done echo Done. } cd $DEPS @@ -775,26 +855,30 @@ fi cd $QT_SDK add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DCMAKE_PREFIX_PATH="$QT_SDK" - if [ $CONFIGURATION == "Debug" ]; then - SUFFIX="d" - else - SUFFIX="" - fi - add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll - add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll" + for CONFIGURATION in ${CONFIGURATIONS[@]}; do + if [ $CONFIGURATION == "Debug" ]; then + DLLSUFFIX="d" + else + DLLSUFFIX="" + fi + add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll + add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll" + done echo Done. else QT_SDK="C:/Qt/5.13/msvc2017${SUFFIX}" add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DCMAKE_PREFIX_PATH="$QT_SDK" - if [ $CONFIGURATION == "Debug" ]; then - SUFFIX="d" - else - SUFFIX="" - fi - DIR=$(windowsPathAsUnix "${QT_SDK}") - add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll - add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll" + for CONFIGURATION in ${CONFIGURATIONS[@]}; do + if [ $CONFIGURATION == "Debug" ]; then + DLLSUFFIX="d" + else + DLLSUFFIX="" + fi + DIR=$(windowsPathAsUnix "${QT_SDK}") + add_runtime_dlls $CONFIGURATION "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll + add_qt_platform_dlls $CONFIGURATION "${DIR}/plugins/platforms/qwindows${DLLSUFFIX}.dll" + done echo Done. fi } @@ -810,7 +894,9 @@ printf "SDL 2.0.12... " eval 7z x -y SDL2-2.0.12.zip $STRIP fi export SDL2DIR="$(real_pwd)/SDL2-2.0.12" - add_runtime_dlls "$(pwd)/SDL2-2.0.12/lib/x${ARCHSUFFIX}/SDL2.dll" + for config in ${CONFIGURATIONS[@]}; do + add_runtime_dlls $config "$(pwd)/SDL2-2.0.12/lib/x${ARCHSUFFIX}/SDL2.dll" + done echo Done. } cd $DEPS @@ -820,41 +906,51 @@ if [ ! -z $TEST_FRAMEWORK ]; then printf "Google test 1.10.0 ..." cd googletest - if [ ! -d build ]; then - mkdir build - fi + mkdir -p build${MSVC_REAL_YEAR} - cd build + cd build${MSVC_REAL_YEAR} GOOGLE_INSTALL_ROOT="${DEPS_INSTALL}/GoogleTest" - if [ $CONFIGURATION == "Debug" ]; then + + for CONFIGURATION in ${CONFIGURATIONS[@]}; do + # FindGMock.cmake mentions Release explicitly, but not RelWithDebInfo. Only one optimised library config can be used, so go for the safer one. + GTEST_CONFIG=$([ $CONFIGURATION == "RelWithDebInfo" ] && echo "Release" || echo "$CONFIGURATION" ) + if [ $GTEST_CONFIG == "Debug" ]; then DEBUG_SUFFIX="d" else DEBUG_SUFFIX="" - fi + fi - if [ ! -d $GOOGLE_INSTALL_ROOT ]; then + if [ ! -f "$GOOGLE_INSTALL_ROOT/lib/gtest${DEBUG_SUFFIX}.lib" ]; then + # Always use MSBuild solution files as they don't need the environment activating + cmake .. -DCMAKE_USE_WIN32_THREADS_INIT=1 -G "Visual Studio $MSVC_REAL_VER $MSVC_REAL_YEAR$([ $BITS -eq 64 ] && [ $MSVC_REAL_VER -lt 16 ] && echo " Win64")" $([ $MSVC_REAL_VER -ge 16 ] && echo "-A $([ $BITS -eq 64 ] && echo "x64" || echo "Win32")") -DBUILD_SHARED_LIBS=1 + cmake --build . --config "${GTEST_CONFIG}" + cmake --install . --config "${GTEST_CONFIG}" --prefix "${GOOGLE_INSTALL_ROOT}" + fi - cmake .. -DCMAKE_BUILD_TYPE="${CONFIGURATION}" -DCMAKE_INSTALL_PREFIX="${GOOGLE_INSTALL_ROOT}" -DCMAKE_USE_WIN32_THREADS_INIT=1 -G "${GENERATOR}" -DBUILD_SHARED_LIBS=1 - cmake --build . --config "${CONFIGURATION}" - cmake --build . --target install --config "${CONFIGURATION}" - - add_runtime_dlls "${GOOGLE_INSTALL_ROOT}\bin\gtest_main${DEBUG_SUFFIX}.dll" - add_runtime_dlls "${GOOGLE_INSTALL_ROOT}\bin\gtest${DEBUG_SUFFIX}.dll" - add_runtime_dlls "${GOOGLE_INSTALL_ROOT}\bin\gmock_main${DEBUG_SUFFIX}.dll" - add_runtime_dlls "${GOOGLE_INSTALL_ROOT}\bin\gmock${DEBUG_SUFFIX}.dll" - fi + add_runtime_dlls $CONFIGURATION "${GOOGLE_INSTALL_ROOT}\bin\gtest_main${DEBUG_SUFFIX}.dll" + add_runtime_dlls $CONFIGURATION "${GOOGLE_INSTALL_ROOT}\bin\gtest${DEBUG_SUFFIX}.dll" + add_runtime_dlls $CONFIGURATION "${GOOGLE_INSTALL_ROOT}\bin\gmock_main${DEBUG_SUFFIX}.dll" + add_runtime_dlls $CONFIGURATION "${GOOGLE_INSTALL_ROOT}\bin\gmock${DEBUG_SUFFIX}.dll" + done add_cmake_opts -DBUILD_UNITTESTS=yes # FindGTest and FindGMock do not work perfectly on Windows # but we can help them by telling them everything we know about installation add_cmake_opts -DGMOCK_ROOT="$GOOGLE_INSTALL_ROOT" add_cmake_opts -DGTEST_ROOT="$GOOGLE_INSTALL_ROOT" - add_cmake_opts -DGTEST_LIBRARY="$GOOGLE_INSTALL_ROOT/lib/gtest${DEBUG_SUFFIX}.lib" - add_cmake_opts -DGTEST_MAIN_LIBRARY="$GOOGLE_INSTALL_ROOT/lib/gtest_main${DEBUG_SUFFIX}.lib" - add_cmake_opts -DGMOCK_LIBRARY="$GOOGLE_INSTALL_ROOT/lib/gmock${DEBUG_SUFFIX}.lib" - add_cmake_opts -DGMOCK_MAIN_LIBRARY="$GOOGLE_INSTALL_ROOT/lib/gmock_main${DEBUG_SUFFIX}.lib" + add_cmake_opts -DGTEST_LIBRARY="$GOOGLE_INSTALL_ROOT/lib/gtest.lib" + add_cmake_opts -DGTEST_MAIN_LIBRARY="$GOOGLE_INSTALL_ROOT/lib/gtest_main.lib" + add_cmake_opts -DGMOCK_LIBRARY="$GOOGLE_INSTALL_ROOT/lib/gmock.lib" + add_cmake_opts -DGMOCK_MAIN_LIBRARY="$GOOGLE_INSTALL_ROOT/lib/gmock_main.lib" + add_cmake_opts -DGTEST_LIBRARY_DEBUG="$GOOGLE_INSTALL_ROOT/lib/gtestd.lib" + add_cmake_opts -DGTEST_MAIN_LIBRARY_DEBUG="$GOOGLE_INSTALL_ROOT/lib/gtest_maind.lib" + add_cmake_opts -DGMOCK_LIBRARY_DEBUG="$GOOGLE_INSTALL_ROOT/lib/gmockd.lib" + add_cmake_opts -DGMOCK_MAIN_LIBRARY_DEBUG="$GOOGLE_INSTALL_ROOT/lib/gmock_maind.lib" add_cmake_opts -DGTEST_LINKED_AS_SHARED_LIBRARY=True + add_cmake_opts -DGTEST_LIBRARY_TYPE=SHARED + add_cmake_opts -DGTEST_MAIN_LIBRARY_TYPE=SHARED + echo Done. fi @@ -901,45 +997,47 @@ if [ ! -z $CI ]; then fi # NOTE: Disable this when/if we want to run test cases #if [ -z $CI ]; then - echo "- Copying Runtime DLLs..." - DLL_PREFIX="" - if [ -z $SINGLE_CONFIG ]; then - mkdir -p $CONFIGURATION - DLL_PREFIX="$CONFIGURATION/" - fi - for DLL in $RUNTIME_DLLS; do - TARGET="$(basename "$DLL")" - if [[ "$DLL" == *":"* ]]; then - originalIFS="$IFS" - IFS=':'; SPLIT=( ${DLL} ); IFS=$originalIFS - DLL=${SPLIT[0]} - TARGET=${SPLIT[1]} + for CONFIGURATION in ${CONFIGURATIONS[@]}; do + echo "- Copying Runtime DLLs for $CONFIGURATION..." + DLL_PREFIX="" + if [ -z $SINGLE_CONFIG ]; then + mkdir -p $CONFIGURATION + DLL_PREFIX="$CONFIGURATION/" fi - echo " ${TARGET}." - cp "$DLL" "${DLL_PREFIX}$TARGET" + for DLL in ${RUNTIME_DLLS[$CONFIGURATION]}; do + TARGET="$(basename "$DLL")" + if [[ "$DLL" == *":"* ]]; then + originalIFS="$IFS" + IFS=':'; SPLIT=( ${DLL} ); IFS=$originalIFS + DLL=${SPLIT[0]} + TARGET=${SPLIT[1]} + fi + echo " ${TARGET}." + cp "$DLL" "${DLL_PREFIX}$TARGET" + done + echo + echo "- OSG Plugin DLLs..." + mkdir -p ${DLL_PREFIX}osgPlugins-3.6.5 + for DLL in ${OSG_PLUGINS[$CONFIGURATION]}; do + echo " $(basename $DLL)." + cp "$DLL" ${DLL_PREFIX}osgPlugins-3.6.5 + done + echo + echo "- Qt Platform DLLs..." + mkdir -p ${DLL_PREFIX}platforms + for DLL in ${QT_PLATFORMS[$CONFIGURATION]}; do + echo " $(basename $DLL)" + cp "$DLL" "${DLL_PREFIX}platforms" + done + echo done - echo - echo "- OSG Plugin DLLs..." - mkdir -p ${DLL_PREFIX}osgPlugins-3.6.5 - for DLL in $OSG_PLUGINS; do - echo " $(basename $DLL)." - cp "$DLL" ${DLL_PREFIX}osgPlugins-3.6.5 - done - echo - echo "- Qt Platform DLLs..." - mkdir -p ${DLL_PREFIX}platforms - for DLL in $QT_PLATFORMS; do - echo " $(basename $DLL)" - cp "$DLL" "${DLL_PREFIX}platforms" - done - echo #fi if [ -n "$ACTIVATE_MSVC" ]; then echo -n "- Activating MSVC in the current shell... " command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } - MSVC_INSTALLATION_PATH=$(vswhere -legacy -products '*' -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) + MSVC_INSTALLATION_PATH=$(vswhere -products '*' -version "[$MSVC_REAL_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) if [ -z "$MSVC_INSTALLATION_PATH" ]; then echo "vswhere was unable to find MSVC $MSVC_DISPLAY_YEAR" wrappedExit 1 From 753ca915569b3531f9543b5806a47de87a273227 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 8 Sep 2020 16:55:12 +0400 Subject: [PATCH 25/50] Set a minimum 1gp cost for services (regression #5600) --- apps/openmw/mwgui/merchantrepair.cpp | 1 + apps/openmw/mwgui/spellbuyingwindow.cpp | 2 +- apps/openmw/mwgui/spellcreationdialog.cpp | 3 ++- apps/openmw/mwgui/trainingwindow.cpp | 5 +++-- apps/openmw/mwgui/travelwindow.cpp | 7 ++++++- apps/openmw/mwmechanics/enchanting.cpp | 2 +- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 282c0e4ea..e737cb2b2 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -63,6 +63,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) int x = static_cast((maxDurability - durability) / r); x = static_cast(fRepairMult * x); + x = std::max(1, x); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mActor, x, true); diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index eea98a768..eb51f560b 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -42,7 +42,7 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int price = static_cast(spell.mData.mCost*store.get().find("fSpellValueMult")->mValue.getFloat()); + int price = std::max(1, static_cast(spell.mData.mCost*store.get().find("fSpellValueMult")->mValue.getFloat())); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); MWWorld::Ptr player = MWMechanics::getPlayer(); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index a567d114b..bfdba8b2e 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -475,7 +475,8 @@ namespace MWGui float fSpellMakingValueMult = store.get().find("fSpellMakingValueMult")->mValue.getFloat(); - int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, static_cast(y * fSpellMakingValueMult),true); + int price = std::max(1, static_cast(y * fSpellMakingValueMult)); + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index d0176ece4..da3c7d186 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -99,8 +99,9 @@ namespace MWGui for (int i=0; i<3; ++i) { - int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer - (mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->mValue.getInteger(),true); + int price = static_cast(pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->mValue.getInteger()); + price = std::max(1, price); + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); MyGUI::Button* button = mTrainingOptions->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 81cd76089..a730f95c6 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -62,9 +62,14 @@ namespace MWGui { ESM::Position PlayerPos = player.getRefData().getPosition(); float d = sqrt(pow(pos.pos[0] - PlayerPos.pos[0], 2) + pow(pos.pos[1] - PlayerPos.pos[1], 2) + pow(pos.pos[2] - PlayerPos.pos[2], 2)); - price = static_cast(d / gmst.find("fTravelMult")->mValue.getFloat()); + float fTravelMult = gmst.find("fTravelMult")->mValue.getFloat(); + if (fTravelMult != 0) + price = static_cast(d / fTravelMult); + else + price = static_cast(d); } + price = std::max(1, price); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); // Add price for the travelling followers diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index c71516090..1717ba06f 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -281,7 +281,7 @@ namespace MWMechanics float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentValueMult")->mValue.getFloat(); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast(getEnchantPoints() * priceMultipler), true); price *= getEnchantItemsCount() * getTypeMultiplier(); - return price; + return std::max(1, price); } int Enchanting::getGemCharge() const From 85b5fdee3573bbe215aee3455763b4de3f751323 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 12 Sep 2020 00:20:44 +0100 Subject: [PATCH 26/50] Discard the alpha channel of 16-bit TGAs, just like Morrowind --- components/resource/imagemanager.cpp | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index 41ce999e0..ff6fb04a6 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -115,6 +115,29 @@ namespace Resource return mWarningImage; } + bool killAlpha = false; + if (reader->supportedExtensions().count("tga")) + { + // Morrowind ignores the alpha channel of 16bpp TGA files even when the header says not to + unsigned char header[18]; + stream->read((char*)header, 18); + if (stream->gcount() != 18) + { + Log(Debug::Error) << "Error loading " << filename << ": couldn't read TGA header"; + mCache->addEntryToObjectCache(normalized, mWarningImage); + return mWarningImage; + } + int type = header[2]; + int depth; + if (type == 1 || type == 9) + depth = header[7]; + else + depth = header[16]; + int alphaBPP = header[17] & 0x0F; + killAlpha = depth == 16 && alphaBPP == 1; + stream->seekg(0); + } + osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, mOptions); if (!result.success()) { @@ -149,6 +172,18 @@ namespace Resource image = newImage; } } + else if (killAlpha) + { + osg::ref_ptr newImage = new osg::Image; + newImage->setFileName(image->getFileName()); + newImage->allocateImage(image->s(), image->t(), image->r(), GL_RGB, GL_UNSIGNED_BYTE); + // OSG just won't write the alpha as there's nowhere to put it. + for (int s = 0; s < image->s(); ++s) + for (int t = 0; t < image->t(); ++t) + for (int r = 0; r < image->r(); ++r) + newImage->setColor(image->getColor(s, t, r), s, t, r); + image = newImage; + } mCache->addEntryToObjectCache(normalized, image); return image; From 58b5249e8bf175916fa18f1c7bb5c5a716c455d9 Mon Sep 17 00:00:00 2001 From: tessa Date: Sun, 13 Sep 2020 10:46:27 -0500 Subject: [PATCH 27/50] fix typo in terrainstorage.hpp --- apps/openmw/mwrender/terrainstorage.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 14bed7b7b..c9ad94398 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -17,7 +17,7 @@ namespace MWRender { public: - TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern = "", const std::string& normalHeightMapPatteern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); + TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern = "", const std::string& normalHeightMapPattern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); ~TerrainStorage(); virtual osg::ref_ptr getLand (int cellX, int cellY) override; From 874c754b68b8af2f8cbf9c5937605e6a8d8237c3 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Mon, 31 Aug 2020 23:16:10 +0200 Subject: [PATCH 28/50] Fix #5557 --- CHANGELOG.md | 1 + apps/openmw/mwinput/actionmanager.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 19 +++++++------------ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c68e7b0f5..00d7f3539 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Bug #5531: Actors flee using current rotation by axis x Bug #5539: Window resize breaks when going from a lower resolution to full screen resolution Bug #5548: Certain exhausted topics can be highlighted again even though there's no new dialogue + Bug #5557: Diagonal movement is noticeably slower with analogue stick Feature #390: 3rd person look "over the shoulder" Feature #2386: Distant Statics in the form of Object Paging Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher diff --git a/apps/openmw/mwinput/actionmanager.cpp b/apps/openmw/mwinput/actionmanager.cpp index d2430a612..b29aa58a2 100644 --- a/apps/openmw/mwinput/actionmanager.cpp +++ b/apps/openmw/mwinput/actionmanager.cpp @@ -141,7 +141,7 @@ namespace MWInput float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight); float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward); - bool isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; + bool isRunning = osg::Vec2f(xAxis * 2 - 1, yAxis * 2 - 1).length2() > 0.25f; if ((mAlwaysRunActive && alwaysRunAllowed) || isRunning) player.setRunState(!mBindingsManager->actionIsActive(A_Run)); else diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 449ac69bb..ad30c16bd 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1954,20 +1954,15 @@ void CharacterController::update(float duration, bool animationOnly) osg::Vec3f rot = cls.getRotationVector(mPtr); osg::Vec3f vec(movementSettings.asVec3()); - - if (isPlayer) - { - // TODO: Move this code to mwinput. - // Joystick analogue movement. - movementSettings.mSpeedFactor = std::max(std::abs(vec.x()), std::abs(vec.y())); - - // Due to the half way split between walking/running, we multiply speed by 2 while walking, unless a keyboard was used. - if(!isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f) - movementSettings.mSpeedFactor *= 2.f; - } else - movementSettings.mSpeedFactor = std::min(vec.length(), 1.f); + movementSettings.mSpeedFactor = std::min(vec.length(), 1.f); vec.normalize(); + // TODO: Move this check to mwinput. + // Joystick analogue movement. + // Due to the half way split between walking/running, we multiply speed by 2 while walking, unless a keyboard was used. + if (isPlayer && !isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f) + movementSettings.mSpeedFactor *= 2.f; + float effectiveRotation = rot.z(); bool canMove = cls.getMaxSpeed(mPtr) > 0; static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game"); From fe82cd9f92a0d6aa7cf3a5f02932b6d8b7fd4490 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 15 Sep 2020 21:13:18 +0200 Subject: [PATCH 29/50] Don't search containers --- apps/openmw/mwworld/worldimp.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f1c8ed73a..9ba24541d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -287,9 +287,9 @@ namespace MWWorld ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. - Ptr searchPtr (const std::string& name, bool activeOnly, bool searchInContainers = true) override; + Ptr searchPtr (const std::string& name, bool activeOnly, bool searchInContainers = false) override; ///< Return a pointer to a liveCellRef with the given name. - /// \param activeOnly do non search inactive cells. + /// \param activeOnly do not search inactive cells. Ptr searchPtrViaActorId (int actorId) override; ///< Search is limited to the active cells. From b2cb98d30e62c78470c479f471f622835828299a Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 16 Sep 2020 23:03:42 +0100 Subject: [PATCH 30/50] Fix NiFlipController NiFlipControllers *always* affect the base texture, even if no base texture is bound. When no base texture is bound, they default to UV set zero and to having wrapped UV coordinates, instead of using the settings for the disabled base texture. --- components/nifosg/controller.cpp | 2 +- components/nifosg/nifloader.cpp | 77 +++++++++++++++++++------------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 58f1c17a7..b6610728a 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -449,7 +449,7 @@ void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *n } FlipController::FlipController(const Nif::NiFlipController *ctrl, const std::vector >& textures) - : mTexSlot(ctrl->mTexSlot) + : mTexSlot(0) // always affects diffuse , mDelta(ctrl->mDelta) , mTextures(textures) { diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 21ae49975..24184f2c8 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -799,22 +799,23 @@ namespace NifOsg { const Nif::NiFlipController* flipctrl = static_cast(ctrl.getPtr()); std::vector > textures; + + // inherit wrap settings from the target slot + osg::Texture2D* inherit = dynamic_cast(stateset->getTextureAttribute(0, osg::StateAttribute::TEXTURE)); + osg::Texture2D::WrapMode wrapS = osg::Texture2D::REPEAT; + osg::Texture2D::WrapMode wrapT = osg::Texture2D::REPEAT; + if (inherit) + { + wrapS = inherit->getWrap(osg::Texture2D::WRAP_S); + wrapT = inherit->getWrap(osg::Texture2D::WRAP_T); + } + for (unsigned int i=0; imSources.length(); ++i) { Nif::NiSourceTexturePtr st = flipctrl->mSources[i]; if (st.empty()) continue; - // inherit wrap settings from the target slot - osg::Texture2D* inherit = dynamic_cast(stateset->getTextureAttribute(flipctrl->mTexSlot, osg::StateAttribute::TEXTURE)); - osg::Texture2D::WrapMode wrapS = osg::Texture2D::CLAMP_TO_EDGE; - osg::Texture2D::WrapMode wrapT = osg::Texture2D::CLAMP_TO_EDGE; - if (inherit) - { - wrapS = inherit->getWrap(osg::Texture2D::WRAP_S); - wrapT = inherit->getWrap(osg::Texture2D::WRAP_T); - } - osg::ref_ptr image (handleSourceTexture(st.getPtr(), imageManager)); osg::ref_ptr texture (new osg::Texture2D(image)); if (image) @@ -1451,7 +1452,7 @@ namespace NifOsg // If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the shadow casting shader will need to be updated accordingly. for (size_t i=0; itextures.size(); ++i) { - if (texprop->textures[i].inUse) + if (texprop->textures[i].inUse || (i == Nif::NiTexturingProperty::BaseTexture && !texprop->controller.empty())) { switch(i) { @@ -1477,32 +1478,44 @@ namespace NifOsg } } - const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i]; - if(tex.texture.empty() && texprop->controller.empty()) - { - if (i == 0) - Log(Debug::Warning) << "Base texture is in use but empty on shape \"" << nodeName << "\" in " << mFilename; - continue; - } - + unsigned int uvSet = 0; // create a new texture, will later attempt to share using the SharedStateManager osg::ref_ptr texture2d; - if (!tex.texture.empty()) + if (texprop->textures[i].inUse) { - 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()); + const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i]; + if(tex.texture.empty() && texprop->controller.empty()) + { + if (i == 0) + Log(Debug::Warning) << "Base texture is in use but empty on shape \"" << nodeName << "\" in " << mFilename; + continue; + } + + if (!tex.texture.empty()) + { + 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; + + bool wrapT = tex.clamp & 0x1; + bool wrapS = (tex.clamp >> 1) & 0x1; + + texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); } else + { + // Texture only comes from NiFlipController, so tex is ignored, set defaults texture2d = new osg::Texture2D; - - bool wrapT = tex.clamp & 0x1; - bool wrapS = (tex.clamp >> 1) & 0x1; - - texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); - texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + texture2d->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + texture2d->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + uvSet = 0; + } unsigned int texUnit = boundTextures.size(); @@ -1590,7 +1603,7 @@ namespace NifOsg break; } - boundTextures.push_back(tex.uvSet); + boundTextures.push_back(uvSet); } } handleTextureControllers(texprop, composite, imageManager, stateset, animflags); From f8f72ce4c4c24c5a1691629270fad64ed5eb1240 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 16 Sep 2020 23:08:01 +0100 Subject: [PATCH 31/50] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00d7f3539..026a65c24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path Bug #1952: Incorrect particle lighting + Bug #2069: Fireflies in Fireflies invade Morrowind look wrong Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects From 3f61ff3a444edcff977ae8882ea9a45b8d67a5f8 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 19 Sep 2020 23:30:34 +0100 Subject: [PATCH 32/50] Make OpenGL debugging optional --- apps/openmw/engine.cpp | 6 ++++-- components/debug/gldebug.cpp | 14 ++++++++++++++ components/debug/gldebug.hpp | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 93b371805..f42dfe3f7 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -516,7 +516,8 @@ void OMW::Engine::createWindow(Settings::Manager& settings) checkSDLError(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24)); - checkSDLError(SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG)); + if (Debug::shouldDebugOpenGL()) + checkSDLError(SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG)); if (antialiasing > 0) { @@ -577,7 +578,8 @@ void OMW::Engine::createWindow(Settings::Manager& settings) camera->setGraphicsContext(graphicsWindow); camera->setViewport(0, 0, traits->width, traits->height); - mViewer->setRealizeOperation(new Debug::EnableGLDebugOperation()); + if (Debug::shouldDebugOpenGL()) + mViewer->setRealizeOperation(new Debug::EnableGLDebugOperation()); mViewer->realize(); diff --git a/components/debug/gldebug.cpp b/components/debug/gldebug.cpp index 76e7a4bb9..20448ccbb 100644 --- a/components/debug/gldebug.cpp +++ b/components/debug/gldebug.cpp @@ -31,6 +31,8 @@ either expressed or implied, of the FreeBSD Project. #include "gldebug.hpp" +#include + #include // OpenGL constants not provided by OSG: @@ -144,3 +146,15 @@ void Debug::EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsCon unsigned int contextID = graphicsContext->getState()->getContextID(); enableGLDebugExtension(contextID); } + +bool Debug::shouldDebugOpenGL() +{ + const char* env = std::getenv("OPENMW_DEBUG_OPENGL"); + if (!env) + return false; + std::string str(env); + if (str.length() == 0) + return true; + + return str.find("OFF") == std::string::npos && str.find("0") == std::string::npos && str.find("NO") == std::string::npos; +} diff --git a/components/debug/gldebug.hpp b/components/debug/gldebug.hpp index 77d6c82a8..823d4f36f 100644 --- a/components/debug/gldebug.hpp +++ b/components/debug/gldebug.hpp @@ -15,5 +15,7 @@ namespace Debug private: OpenThreads::Mutex mMutex; }; + + bool shouldDebugOpenGL(); } #endif From 22c3588d0d9ac14f449008232463af876e367c88 Mon Sep 17 00:00:00 2001 From: psi29a Date: Sun, 20 Sep 2020 12:22:31 +0000 Subject: [PATCH 33/50] Merge branch '4771-and-4631' into 'master' Try lower MSAA level if the requested value isn't available Closes #4471 and #4631 See merge request OpenMW/openmw!297 (cherry picked from commit b3db387512340a5e9a77427c2d7d9d88c2340056) da0aef7a Retrieve SDL OpenGL attributes after context creation a51e63b3 Try lower MSAA levels if OpenGL context doesn't have what we requested c4e92a0a Update CHANGELOG.md --- CHANGELOG.md | 1 + apps/openmw/engine.cpp | 103 +++++++++++++---------- components/sdlutil/sdlgraphicswindow.cpp | 23 +++++ 3 files changed, 83 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 026a65c24..dfb6532f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Bug #4021: Attributes and skills are not stored as floats Bug #4055: Local scripts don't inherit variables from their base record Bug #4623: Corprus implementation is incorrect + Bug #4631: Setting MSAA level too high doesn't fall back to highest supported level Bug #4764: Data race in osg ParticleSystem Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5108: Savegame bloating due to inefficient fog textures format diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f42dfe3f7..5f0884a9e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -525,65 +525,80 @@ void OMW::Engine::createWindow(Settings::Manager& settings) checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing)); } - while (!mWindow) + osg::ref_ptr graphicsWindow; + while (!graphicsWindow || !graphicsWindow->valid()) { - mWindow = SDL_CreateWindow("OpenMW", pos_x, pos_y, width, height, flags); - if (!mWindow) + while (!mWindow) { - // Try with a lower AA - if (antialiasing > 0) + mWindow = SDL_CreateWindow("OpenMW", pos_x, pos_y, width, height, flags); + if (!mWindow) { - Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2; - antialiasing /= 2; - Settings::Manager::setInt("antialiasing", "Video", antialiasing); - checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing)); - continue; - } - else - { - std::stringstream error; - error << "Failed to create SDL window: " << SDL_GetError(); - throw std::runtime_error(error.str()); + // Try with a lower AA + if (antialiasing > 0) + { + Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2; + antialiasing /= 2; + Settings::Manager::setInt("antialiasing", "Video", antialiasing); + checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing)); + continue; + } + else + { + std::stringstream error; + error << "Failed to create SDL window: " << SDL_GetError(); + throw std::runtime_error(error.str()); + } } } + + setWindowIcon(); + + osg::ref_ptr traits = new osg::GraphicsContext::Traits; + SDL_GetWindowPosition(mWindow, &traits->x, &traits->y); + SDL_GetWindowSize(mWindow, &traits->width, &traits->height); + traits->windowName = SDL_GetWindowTitle(mWindow); + traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS); + traits->screenNum = SDL_GetWindowDisplayIndex(mWindow); + traits->vsync = vsync; + traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow); + + graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits); + if (!graphicsWindow->valid()) throw std::runtime_error("Failed to create GraphicsContext"); + + if (traits->samples < antialiasing) + { + Log(Debug::Warning) << "Warning: Framebuffer MSAA level is only " << traits->samples << "x instead of " << antialiasing << "x. Trying " << antialiasing / 2 << "x instead."; + graphicsWindow->closeImplementation(); + SDL_DestroyWindow(mWindow); + mWindow = nullptr; + antialiasing /= 2; + Settings::Manager::setInt("antialiasing", "Video", antialiasing); + checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing)); + continue; + } + + if (traits->red < 8) + Log(Debug::Warning) << "Warning: Framebuffer only has a " << traits->red << " bit red channel."; + if (traits->green < 8) + Log(Debug::Warning) << "Warning: Framebuffer only has a " << traits->green << " bit green channel."; + if (traits->blue < 8) + Log(Debug::Warning) << "Warning: Framebuffer only has a " << traits->blue << " bit blue channel."; + if (traits->depth < 8) + Log(Debug::Warning) << "Warning: Framebuffer only has " << traits->red << " bits of depth precision."; + + traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel } - setWindowIcon(); - - osg::ref_ptr traits = new osg::GraphicsContext::Traits; - SDL_GetWindowPosition(mWindow, &traits->x, &traits->y); - SDL_GetWindowSize(mWindow, &traits->width, &traits->height); - traits->windowName = SDL_GetWindowTitle(mWindow); - traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS); - traits->screenNum = SDL_GetWindowDisplayIndex(mWindow); - // We tried to get rid of the hardcoding but failed: https://github.com/OpenMW/openmw/pull/1771 - // Here goes kcat's quote: - // It's ultimately a chicken and egg problem, and the reason why the code is like it was in the first place. - // It needs a context to get the current attributes, but it needs the attributes to set up the context. - // So it just specifies the same values that were given to SDL in the hopes that it's good enough to what the window eventually gets. - traits->red = 8; - traits->green = 8; - traits->blue = 8; - traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel - traits->depth = 24; - traits->stencil = 8; - traits->vsync = vsync; - traits->doubleBuffer = true; - traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow); - - osg::ref_ptr graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits); - if(!graphicsWindow->valid()) throw std::runtime_error("Failed to create GraphicsContext"); - osg::ref_ptr camera = mViewer->getCamera(); camera->setGraphicsContext(graphicsWindow); - camera->setViewport(0, 0, traits->width, traits->height); + camera->setViewport(0, 0, graphicsWindow->getTraits()->width, graphicsWindow->getTraits()->height); if (Debug::shouldDebugOpenGL()) mViewer->setRealizeOperation(new Debug::EnableGLDebugOperation()); mViewer->realize(); - mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, traits->width, traits->height); + mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, graphicsWindow->getTraits()->width, graphicsWindow->getTraits()->height); } void OMW::Engine::setWindowIcon() diff --git a/components/sdlutil/sdlgraphicswindow.cpp b/components/sdlutil/sdlgraphicswindow.cpp index cd5e80c31..0a1951700 100644 --- a/components/sdlutil/sdlgraphicswindow.cpp +++ b/components/sdlutil/sdlgraphicswindow.cpp @@ -118,6 +118,29 @@ void GraphicsWindowSDL2::init() setSwapInterval(_traits->vsync); + // Update traits with what we've actually been given + // Use intermediate to avoid signed/unsigned mismatch + int intermediateLocation; + SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &intermediateLocation); + _traits->red = intermediateLocation; + SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &intermediateLocation); + _traits->green = intermediateLocation; + SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &intermediateLocation); + _traits->blue = intermediateLocation; + SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &intermediateLocation); + _traits->alpha = intermediateLocation; + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &intermediateLocation); + _traits->depth = intermediateLocation; + SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &intermediateLocation); + _traits->stencil = intermediateLocation; + + SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &intermediateLocation); + _traits->doubleBuffer = intermediateLocation; + SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &intermediateLocation); + _traits->sampleBuffers = intermediateLocation; + SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &intermediateLocation); + _traits->samples = intermediateLocation; + SDL_GL_MakeCurrent(oldWin, oldCtx); mValid = true; From 390fb4f12e162e63a4a3028b240770943e6a4b87 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 21 Sep 2020 00:49:09 +0100 Subject: [PATCH 34/50] Disable OpenGL Debug stuff when SDL doesn't provide the necessary definitions --- components/debug/gldebug.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/debug/gldebug.cpp b/components/debug/gldebug.cpp index 20448ccbb..3c5ec728a 100644 --- a/components/debug/gldebug.cpp +++ b/components/debug/gldebug.cpp @@ -40,6 +40,7 @@ either expressed or implied, of the FreeBSD Project. void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) { +#ifdef GL_DEBUG_OUTPUT std::string srcStr; switch (source) { @@ -96,10 +97,12 @@ void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsiz } Log(logSeverity) << "OpenGL " << typeStr << " [" << srcStr << "]: " << message; +#endif } void enableGLDebugExtension(unsigned int contextID) { +#ifdef GL_DEBUG_OUTPUT typedef void (GL_APIENTRY *DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); typedef void (GL_APIENTRY *GLDebugMessageControlFunction)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); typedef void (GL_APIENTRY *GLDebugMessageCallbackFunction)(DEBUGPROC, const void* userParam); @@ -132,6 +135,7 @@ void enableGLDebugExtension(unsigned int contextID) Log(Debug::Info) << "OpenGL debug callback attached."; } else +#endif Log(Debug::Error) << "Unable to attach OpenGL debug callback."; } From 7d776609c81daa0d60e899d0c27cb82592ecb371 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 21 Sep 2020 10:31:48 +0400 Subject: [PATCH 35/50] Fix unsugned/signed comparison warning --- apps/openmw/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5f0884a9e..012ab8f59 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -490,7 +490,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings) bool fullscreen = settings.getBool("fullscreen", "Video"); bool windowBorder = settings.getBool("window border", "Video"); bool vsync = settings.getBool("vsync", "Video"); - int antialiasing = settings.getInt("antialiasing", "Video"); + unsigned int antialiasing = std::max(0, settings.getInt("antialiasing", "Video")); int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(screen), pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(screen); From 387bda5e2207023dedf23630ca90775169ca8399 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Tue, 8 Sep 2020 22:49:02 +0300 Subject: [PATCH 36/50] Add inverse order rotation support to SetAngle (feature #5579) --- CHANGELOG.md | 1 + apps/openmw/mwscript/transformationextensions.cpp | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfb6532f3..2287943ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ Feature #5524: Resume failed script execution after reload Feature #5525: Search fields tweaks (utf-8) Feature #5545: Option to allow stealing from an unconscious NPC during combat + Feature #5579: MCP SetAngle enhancement Task #5480: Drop Qt4 support Task #5520: Improve cell name autocompleter implementation diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 7bb9093c1..3bc8cb1f0 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -159,12 +159,20 @@ namespace MWScript float ay = ptr.getRefData().getPosition().rot[1]; float az = ptr.getRefData().getPosition().rot[2]; + // XYZ axis use the inverse (XYZ) rotation order like vanilla SetAngle. + // UWV axis use the standard (ZYX) rotation order like TESCS/OpenMW-CS and the rest of the game. if (axis == "x") - MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); + MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az,MWBase::RotationFlag_inverseOrder); else if (axis == "y") - MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az,MWBase::RotationFlag_inverseOrder); else if (axis == "z") - MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle,MWBase::RotationFlag_inverseOrder); + else if (axis == "u") + MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az,MWBase::RotationFlag_none); + else if (axis == "w") + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az,MWBase::RotationFlag_none); + else if (axis == "v") + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle,MWBase::RotationFlag_none); } }; From f8389c6c37d3658cee659bf8fa09623a72fc7ca0 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Tue, 22 Sep 2020 00:33:27 +0300 Subject: [PATCH 37/50] Support enchantment absorption --- apps/openmw/mwmechanics/spellabsorption.cpp | 23 +++++++++++++++++---- apps/openmw/mwmechanics/spellabsorption.hpp | 7 ++----- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/spellabsorption.cpp b/apps/openmw/mwmechanics/spellabsorption.cpp index f22cef3f6..71e1d0aee 100644 --- a/apps/openmw/mwmechanics/spellabsorption.cpp +++ b/apps/openmw/mwmechanics/spellabsorption.cpp @@ -12,6 +12,7 @@ #include "../mwworld/inventorystore.hpp" #include "creaturestats.hpp" +#include "spellutil.hpp" namespace MWMechanics { @@ -43,9 +44,9 @@ namespace MWMechanics } }; - bool absorbSpell (const ESM::Spell* spell, const MWWorld::Ptr& caster, const MWWorld::Ptr& target) + bool absorbSpell (const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target) { - if (!spell || caster == target || !target.getClass().isActor()) + if (spellId.empty() || caster == target || !target.getClass().isActor()) return false; CreatureStats& stats = target.getClass().getCreatureStats(target); @@ -62,13 +63,27 @@ namespace MWMechanics if (Misc::Rng::roll0to99() >= chance) return false; - const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find("VFX_Absorb"); + const auto& esmStore = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Static* absorbStatic = esmStore.get().find("VFX_Absorb"); MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target); if (animation && !absorbStatic->mModel.empty()) animation->addEffect( "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, std::string()); + const ESM::Spell* spell = esmStore.get().search(spellId); + int spellCost = 0; + if (spell) + { + spellCost = spell->mData.mCost; + } + else + { + const ESM::Enchantment* enchantment = esmStore.get().search(spellId); + if (enchantment) + spellCost = getEffectiveEnchantmentCastCost(static_cast(enchantment->mData.mCost), caster); + } + // Magicka is increased by the cost of the spell DynamicStat magicka = stats.getMagicka(); - magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); + magicka.setCurrent(magicka.getCurrent() + spellCost); stats.setMagicka(magicka); return true; } diff --git a/apps/openmw/mwmechanics/spellabsorption.hpp b/apps/openmw/mwmechanics/spellabsorption.hpp index 147090d96..0fe501df9 100644 --- a/apps/openmw/mwmechanics/spellabsorption.hpp +++ b/apps/openmw/mwmechanics/spellabsorption.hpp @@ -1,10 +1,7 @@ #ifndef MWMECHANICS_SPELLABSORPTION_H #define MWMECHANICS_SPELLABSORPTION_H -namespace ESM -{ - struct Spell; -} +#include namespace MWWorld { @@ -14,7 +11,7 @@ namespace MWWorld namespace MWMechanics { // Try to absorb a spell based on the magnitude of every Spell Absorption effect source on the target. - bool absorbSpell(const ESM::Spell* spell, const MWWorld::Ptr& caster, const MWWorld::Ptr& target); + bool absorbSpell(const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target); } #endif diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 3767acb3f..81b3a353d 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -119,7 +119,7 @@ namespace MWMechanics // effects, we display a "can't re-cast" message // Try absorbing the spell. Some handling must still happen for absorbed effects. - bool absorbed = absorbSpell(spell, caster, target); + bool absorbed = absorbSpell(mId, caster, target); int currentEffectIndex = 0; for (std::vector::const_iterator effectIt (effects.mList.begin()); From 71ba94a89ad7f5980df6adb7db0abcf56a843f81 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sat, 29 Aug 2020 10:33:57 +0200 Subject: [PATCH 38/50] Smooth turning; smooth stopping; combat headtracking --- apps/openmw/mwmechanics/actors.cpp | 24 ++++++++++++++++++----- apps/openmw/mwmechanics/aibreathe.cpp | 2 +- apps/openmw/mwmechanics/aipackage.cpp | 26 ++++++++++++++++++++++++- apps/openmw/mwmechanics/aiwander.cpp | 1 + apps/openmw/mwmechanics/pathfinding.cpp | 23 ++++++++++++++++++++++ apps/openmw/mwmechanics/steering.cpp | 24 ++++++++++------------- files/settings-default.cfg | 3 +++ 7 files changed, 82 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0dfa7a9e6..0b268214b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -473,6 +473,10 @@ namespace MWMechanics void Actors::updateMovementSpeed(const MWWorld::Ptr& actor) { + static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game"); + if (smoothMovement) + return; + CreatureStats &stats = actor.getClass().getCreatureStats(actor); MWMechanics::AiSequence& seq = stats.getAiSequence(); @@ -481,9 +485,10 @@ namespace MWMechanics osg::Vec3f targetPos = seq.getActivePackage().getDestination(); osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3(); float distance = (targetPos - actorPos).length(); + if (distance < DECELERATE_DISTANCE) { - float speedCoef = std::max(0.7f, 0.1f * (distance/64.f + 2.f)); + float speedCoef = std::max(0.7f, 0.2f + 0.8f * distance / DECELERATE_DISTANCE); auto& movement = actor.getClass().getMovementSettings(actor); movement.mPosition[0] *= speedCoef; movement.mPosition[1] *= speedCoef; @@ -1769,14 +1774,12 @@ namespace MWMechanics MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); bool firstPersonPlayer = isPlayer && world->isFirstPerson(); + bool inCombatOrPursue = stats.getAiSequence().isInCombat() || stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue); // 1. Unconsious actor can not track target // 2. Actors in combat and pursue mode do not bother to headtrack // 3. Player character does not use headtracking in the 1st-person view - if (!stats.getKnockedDown() && - !stats.getAiSequence().isInCombat() && - !stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue) && - !firstPersonPlayer) + if (!stats.getKnockedDown() && !firstPersonPlayer && !inCombatOrPursue) { for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { @@ -1786,6 +1789,17 @@ namespace MWMechanics } } + if (!stats.getKnockedDown() && !isPlayer && inCombatOrPursue) + { + // Actors in combat and pursue mode always look at their target. + for (const auto& package : stats.getAiSequence()) + { + headTrackTarget = package->getTarget(); + if (!headTrackTarget.isEmpty()) + break; + } + } + ctrl->setHeadTrackTarget(headTrackTarget); } diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index 15251e125..2740355b5 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -23,7 +23,7 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); actorClass.getMovementSettings(actor).mPosition[1] = 1; - smoothTurn(actor, -180, 0); + smoothTurn(actor, -osg::PI / 2, 0); return false; } diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 53e366579..4bffd28ba 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -87,6 +88,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& //... But AI processing distance may increase in the future. if (isNearInactiveCell(position)) { + actor.getClass().getMovementSettings(actor).mPosition[0] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0; world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest); return false; @@ -169,12 +171,34 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& } // turn to next path point by X,Z axes - zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y())); + float zAngleToNext = mPathFinder.getZAngleToNext(position.x(), position.y()); + zTurn(actor, zAngleToNext); smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0); const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front(); mObstacleCheck.update(actor, destination, duration); + static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game"); + if (smoothMovement) + { + const float smoothTurnReservedDist = 150; + auto& movement = actor.getClass().getMovementSettings(actor); + float distToNextSqr = osg::Vec2f(destination.x() - position.x(), destination.y() - position.y()).length2(); + float diffAngle = zAngleToNext - actor.getRefData().getPosition().rot[2]; + if (std::cos(diffAngle) < -0.1) + movement.mPosition[0] = movement.mPosition[1] = 0; + else if (distToNextSqr > smoothTurnReservedDist * smoothTurnReservedDist) + { // Go forward (and slowly turn towards the next path point) + movement.mPosition[0] = 0; + movement.mPosition[1] = 1; + } + else + { // Next path point is near, so use diagonal movement to follow the path precisely. + movement.mPosition[0] = std::sin(diffAngle); + movement.mPosition[1] = std::max(std::cos(diffAngle), 0.f); + } + } + // handle obstacles on the way evadeObstacles(actor); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 9e179edeb..73ede04fd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -96,6 +96,7 @@ namespace MWMechanics void stopMovement(const MWWorld::Ptr& actor) { + actor.getClass().getMovementSettings(actor).mPosition[0] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0; } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 4d4b5be51..a82dcf717 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -88,6 +88,24 @@ namespace const auto halfExtents = world->getHalfExtents(actor); return 2.0 * halfExtents.z(); } + + // Returns true if turn in `p2` is less than 10 degrees and all the 3 points are almost on one line. + bool isAlmostStraight(const osg::Vec3f& p1, const osg::Vec3f& p2, const osg::Vec3f& p3, float pointTolerance) { + osg::Vec3f v1 = p1 - p2; + osg::Vec3f v3 = p3 - p2; + v1.z() = v3.z() = 0; + float dotProduct = v1.x() * v3.x() + v1.y() * v3.y(); + float crossProduct = v1.x() * v3.y() - v1.y() * v3.x(); + + // Check that the angle between v1 and v3 is less or equal than 10 degrees. + static const float cos170 = std::cos(osg::PI / 180 * 170); + bool checkAngle = dotProduct <= cos170 * v1.length() * v3.length(); + + // Check that distance from p2 to the line (p1, p3) is less or equal than `pointTolerance`. + bool checkDist = std::abs(crossProduct) <= pointTolerance * (p3 - p1).length() * 2; + + return checkAngle && checkDist; + } } namespace MWMechanics @@ -286,6 +304,11 @@ namespace MWMechanics while (mPath.size() > 1 && sqrDistanceIgnoreZ(mPath.front(), position) < pointTolerance * pointTolerance) mPath.pop_front(); + while (mPath.size() > 2 && isAlmostStraight(mPath[0], mPath[1], mPath[2], pointTolerance)) + mPath.erase(mPath.begin() + 1); + if (mPath.size() > 1 && isAlmostStraight(position, mPath[0], mPath[1], pointTolerance)) + mPath.pop_front(); + if (mPath.size() == 1 && sqrDistanceIgnoreZ(mPath.front(), position) < destinationTolerance * destinationTolerance) mPath.pop_front(); } diff --git a/apps/openmw/mwmechanics/steering.cpp b/apps/openmw/mwmechanics/steering.cpp index d442085ea..eaf37fbd2 100644 --- a/apps/openmw/mwmechanics/steering.cpp +++ b/apps/openmw/mwmechanics/steering.cpp @@ -1,5 +1,8 @@ #include "steering.hpp" +#include +#include + #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" @@ -12,19 +15,8 @@ namespace MWMechanics bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, float epsilonRadians) { - float currentAngle (actor.getRefData().getPosition().rot[axis]); - float diff (targetAngleRadians - currentAngle); - if (std::abs(diff) >= osg::DegreesToRadians(180.f)) - { - if (diff >= 0) - { - diff = diff - osg::DegreesToRadians(360.f); - } - else - { - diff = osg::DegreesToRadians(360.f) + diff; - } - } + MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor); + float diff = Misc::normalizeAngle(targetAngleRadians - actor.getRefData().getPosition().rot[axis]); float absDiff = std::abs(diff); // The turning animation actually moves you slightly, so the angle will be wrong again. @@ -33,10 +25,14 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f return true; float limit = getAngularVelocity(actor.getClass().getMaxSpeed(actor)) * MWBase::Environment::get().getFrameDuration(); + static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game"); + if (smoothMovement) + limit *= std::min(absDiff / osg::PI + 0.1, 0.5); + if (absDiff > limit) diff = osg::sign(diff) * limit; - actor.getClass().getMovementSettings(actor).mRotation[axis] = diff; + movement.mRotation[axis] = diff; return false; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index ac5433f30..63df884e5 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -325,6 +325,9 @@ uncapped damage fatigue = false # Turn lower body to movement direction. 'true' makes diagonal movement more realistic. turn to movement direction = false +# Makes all movements of NPCs and player more smooth. +smooth movement = false + # Makes player swim a bit upward from the line of sight. swim upward correction = false From bd6b984022458395a5672d1a680f0f18fb1a5c32 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Fri, 4 Sep 2020 15:03:33 +0200 Subject: [PATCH 39/50] Smoothing speed in CharacterController --- apps/openmw/mwmechanics/aiwander.cpp | 3 ++ apps/openmw/mwmechanics/character.cpp | 50 ++++++++++++++++++++++++--- apps/openmw/mwmechanics/character.hpp | 1 + 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 73ede04fd..cef97fa9b 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -469,6 +469,9 @@ namespace MWMechanics void AiWander::onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage) { + // Wait while fully stop before starting idle animation (important if "smooth movement" is enabled). + if (actor.getClass().getCurrentSpeed(actor) > 0) + return; unsigned short idleAnimation = getRandomIdle(); storage.mIdleAnimation = idleAnimation; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ad30c16bd..2407788e7 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1963,6 +1963,50 @@ void CharacterController::update(float duration, bool animationOnly) if (isPlayer && !isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f) movementSettings.mSpeedFactor *= 2.f; + static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game"); + if (smoothMovement && !isFirstPersonPlayer) + { + float angle = mPtr.getRefData().getPosition().rot[2]; + osg::Vec2f targetSpeed = Misc::rotateVec2f(osg::Vec2f(vec.x(), vec.y()), -angle) * movementSettings.mSpeedFactor; + osg::Vec2f delta = targetSpeed - mSmoothedSpeed; + float speedDelta = movementSettings.mSpeedFactor - mSmoothedSpeed.length(); + float deltaLen = delta.length(); + + float maxDelta; + if (std::abs(speedDelta) < deltaLen / 2) + // Turning is smooth for player and less smooth for NPCs (otherwise NPC can miss a path point). + maxDelta = duration * (isPlayer ? 3.f : 6.f); + else if (isPlayer && speedDelta < -deltaLen / 2) + // As soon as controls are released, mwinput switches player from running to walking. + // So stopping should be instant for player, otherwise it causes a small twitch. + maxDelta = 1; + else // In all other cases speeding up and stopping are smooth. + maxDelta = duration * 3.f; + + if (deltaLen > maxDelta) + delta *= maxDelta / deltaLen; + mSmoothedSpeed += delta; + + osg::Vec2f newSpeed = Misc::rotateVec2f(mSmoothedSpeed, angle); + movementSettings.mSpeedFactor = newSpeed.normalize(); + vec.x() = newSpeed.x(); + vec.y() = newSpeed.y(); + + const float eps = 0.001f; + if (movementSettings.mSpeedFactor < eps) + { + movementSettings.mSpeedFactor = 0; + vec.x() = 0; + vec.y() = 1; + } + else if ((vec.y() < 0) != mIsMovingBackward) + { + if (targetSpeed.length() < eps || (movementSettings.mPosition[1] < 0) == mIsMovingBackward) + vec.y() = mIsMovingBackward ? -eps : eps; + } + vec.normalize(); + } + float effectiveRotation = rot.z(); bool canMove = cls.getMaxSpeed(mPtr) > 0; static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game"); @@ -2185,13 +2229,11 @@ void CharacterController::update(float duration, bool animationOnly) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); } - else if (effectiveRotation != 0.0f) + else { // Do not play turning animation for player if rotation speed is very slow. // Actual threshold should take framerate in account. - float rotationThreshold = 0.f; - if (isPlayer) - rotationThreshold = 0.015 * 60 * duration; + float rotationThreshold = (isPlayer ? 0.015f : 0.001f) * 60 * duration; // It seems only bipedal actors use turning animations. // Also do not use turning animations in the first-person view and when sneaking. diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 6092ca724..0336c4e69 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -196,6 +196,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener float mTimeUntilWake; bool mIsMovingBackward; + osg::Vec2f mSmoothedSpeed; void setAttackTypeBasedOnMovement(); From 6a75942907e162cad9f22119d3b8d1bf7ef8eac1 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Mon, 31 Aug 2020 01:25:53 +0200 Subject: [PATCH 40/50] Fix CharacterController::setAttackTypeBasedOnMovement --- apps/openmw/mwmechanics/character.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2407788e7..f7d745250 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2737,10 +2737,9 @@ void CharacterController::setVisibility(float visibility) void CharacterController::setAttackTypeBasedOnMovement() { float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition; - - if (move[1] && !move[0]) // forward-backward + if (std::abs(move[1]) > std::abs(move[0]) + 0.2f) // forward-backward mAttackType = "thrust"; - else if (move[0] && !move[1]) //sideway + else if (std::abs(move[0]) > std::abs(move[1]) + 0.2f) // sideway mAttackType = "slash"; else mAttackType = "chop"; From 79a72e4b44a3f873e61ce485bc1838985b16fb7e Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Fri, 4 Sep 2020 14:14:56 +0200 Subject: [PATCH 41/50] Slightly modify aicombat to make it more difficult to get around an enemy. The difference is visible only if turning is slow (i.e. if smooth movement enabled). --- apps/openmw/mwmechanics/aicombat.cpp | 58 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 1ae27a9cb..cbe161af1 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -5,6 +5,8 @@ #include +#include + #include #include "../mwphysics/collisiontype.hpp" @@ -240,10 +242,6 @@ namespace MWMechanics if (storage.mReadyToAttack) { - storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target); - // start new attack - storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat); - if (isRangedCombat) { // rotate actor taking into account target movement direction and projectile speed @@ -259,6 +257,10 @@ namespace MWMechanics storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir); storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated } + + storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target); + // start new attack + storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat); } return false; } @@ -372,9 +374,13 @@ namespace MWMechanics void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage) { // apply combat movement + float deltaAngle = storage.mMovement.mRotation[2] - actor.getRefData().getPosition().rot[2]; + osg::Vec2f movement = Misc::rotateVec2f( + osg::Vec2f(storage.mMovement.mPosition[0], storage.mMovement.mPosition[1]), -deltaAngle); + MWMechanics::Movement& actorMovementSettings = actor.getClass().getMovementSettings(actor); - actorMovementSettings.mPosition[0] = storage.mMovement.mPosition[0]; - actorMovementSettings.mPosition[1] = storage.mMovement.mPosition[1]; + actorMovementSettings.mPosition[0] = movement.x(); + actorMovementSettings.mPosition[1] = movement.y(); actorMovementSettings.mPosition[2] = storage.mMovement.mPosition[2]; rotateActorOnAxis(actor, 2, actorMovementSettings, storage); @@ -385,26 +391,11 @@ namespace MWMechanics MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage) { actorMovementSettings.mRotation[axis] = 0; - float& targetAngleRadians = storage.mMovement.mRotation[axis]; - if (targetAngleRadians != 0) - { - // Some attack animations contain small amount of movement. - // Since we use cone shapes for melee, we can use a threshold to avoid jittering - std::shared_ptr& currentAction = storage.mCurrentAction; - bool isRangedCombat = false; - currentAction->getCombatRange(isRangedCombat); - // Check if the actor now facing desired direction, no need to turn any more - if (isRangedCombat) - { - if (smoothTurn(actor, targetAngleRadians, axis)) - targetAngleRadians = 0; - } - else - { - if (smoothTurn(actor, targetAngleRadians, axis, osg::DegreesToRadians(3.f))) - targetAngleRadians = 0; - } - } + bool isRangedCombat = false; + storage.mCurrentAction->getCombatRange(isRangedCombat); + float eps = isRangedCombat ? osg::DegreesToRadians(0.5) : osg::DegreesToRadians(3.f); + float targetAngleRadians = storage.mMovement.mRotation[axis]; + smoothTurn(actor, targetAngleRadians, axis, eps); } MWWorld::Ptr AiCombat::getTarget() const @@ -489,12 +480,19 @@ namespace MWMechanics // Note: do not use for ranged combat yet since in couple with back up behaviour can move actor out of cliff else if (actor.getClass().isBipedal(actor)) { - // apply sideway movement (kind of dodging) with some probability - // if actor is within range of target's weapon - if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25) + float moveDuration = 0; + float angleToTarget = Misc::normalizeAngle(mMovement.mRotation[2] - actor.getRefData().getPosition().rot[2]); + // Apply a big side step if enemy tries to get around and come from behind. + // Otherwise apply a random side step (kind of dodging) with some probability + // if actor is within range of target's weapon. + if (std::abs(angleToTarget) > osg::PI / 4) + moveDuration = 0.2; + else if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25) + moveDuration = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); + if (moveDuration > 0) { mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right - mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); + mTimerCombatMove = moveDuration; mCombatMove = true; } } From b8387825575a2cf78c619de7697187e98e5960cb Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Mon, 31 Aug 2020 01:12:52 +0200 Subject: [PATCH 42/50] Avoid collisions between actors. --- apps/openmw/mwmechanics/actors.cpp | 131 +++++++++++++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 2 + apps/openmw/mwmechanics/aiwander.cpp | 29 ++++-- apps/openmw/mwmechanics/aiwander.hpp | 7 +- files/settings-default.cfg | 6 ++ 5 files changed, 165 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0b268214b..02b71e3d5 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "../mwworld/esmstore.hpp" @@ -36,6 +37,7 @@ #include "aicombataction.hpp" #include "aifollow.hpp" #include "aipursue.hpp" +#include "aiwander.hpp" #include "actor.hpp" #include "summoning.hpp" #include "combat.hpp" @@ -1664,6 +1666,131 @@ namespace MWMechanics } + void Actors::predictAndAvoidCollisions() + { + const float minGap = 10.f; + const float maxDistToCheck = 100.f; + const float maxTimeToCheck = 1.f; + static const bool giveWayWhenIdle = Settings::Manager::getBool("NPCs give way", "Game"); + + MWWorld::Ptr player = getPlayer(); + MWBase::World* world = MWBase::Environment::get().getWorld(); + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + { + const MWWorld::Ptr& ptr = iter->first; + if (ptr == player) + continue; // Don't interfere with player controls. + + Movement& movement = ptr.getClass().getMovementSettings(ptr); + osg::Vec2f origMovement(movement.mPosition[0], movement.mPosition[1]); + bool isMoving = origMovement.length2() > 0.01; + + // Moving NPCs always should avoid collisions. + // Standing NPCs give way to moving ones if they are not in combat (or pursue) mode and either + // follow player or have a AIWander package with non-empty wander area. + bool shouldAvoidCollision = isMoving; + bool shouldTurnToApproachingActor = !isMoving; + MWWorld::Ptr currentTarget; // Combat or pursue target (NPCs should not avoid collision with their targets). + for (const auto& package : ptr.getClass().getCreatureStats(ptr).getAiSequence()) + { + if (package->getTypeId() == AiPackageTypeId::Follow) + shouldAvoidCollision = true; + else if (package->getTypeId() == AiPackageTypeId::Wander && giveWayWhenIdle) + { + if (!dynamic_cast(package.get())->isStationary()) + shouldAvoidCollision = true; + } + else if (package->getTypeId() == AiPackageTypeId::Combat || package->getTypeId() == AiPackageTypeId::Pursue) + { + currentTarget = package->getTarget(); + shouldAvoidCollision = isMoving; + shouldTurnToApproachingActor = false; + break; + } + } + if (!shouldAvoidCollision) + continue; + + float maxSpeed = ptr.getClass().getMaxSpeed(ptr); + osg::Vec2f baseSpeed = origMovement * maxSpeed; + osg::Vec3f basePos = ptr.getRefData().getPosition().asVec3(); + float baseRotZ = ptr.getRefData().getPosition().rot[2]; + osg::Vec3f halfExtents = world->getHalfExtents(ptr); + + float timeToCollision = maxTimeToCheck; + osg::Vec2f movementCorrection(0, 0); + float angleToApproachingActor = 0; + + // Iterate through all other actors and predict collisions. + for(PtrActorMap::iterator otherIter(mActors.begin()); otherIter != mActors.end(); ++otherIter) + { + const MWWorld::Ptr& otherPtr = otherIter->first; + if (otherPtr == ptr || otherPtr == currentTarget) + continue; + + osg::Vec3f otherHalfExtents = world->getHalfExtents(otherPtr); + osg::Vec3f deltaPos = otherPtr.getRefData().getPosition().asVec3() - basePos; + osg::Vec2f relPos = Misc::rotateVec2f(osg::Vec2f(deltaPos.x(), deltaPos.y()), baseRotZ); + + // Ignore actors which are not close enough or come from behind. + if (deltaPos.length2() > maxDistToCheck * maxDistToCheck || relPos.y() < 0) + continue; + + // Don't check for a collision if vertical distance is greater then the actor's height. + if (deltaPos.z() > halfExtents.z() * 2 || deltaPos.z() < -otherHalfExtents.z() * 2) + continue; + + osg::Vec3f speed = otherPtr.getClass().getMovementSettings(otherPtr).asVec3() * + otherPtr.getClass().getMaxSpeed(otherPtr); + float rotZ = otherPtr.getRefData().getPosition().rot[2]; + osg::Vec2f relSpeed = Misc::rotateVec2f(osg::Vec2f(speed.x(), speed.y()), baseRotZ - rotZ) - baseSpeed; + + float collisionDist = minGap + world->getHalfExtents(ptr).x() + world->getHalfExtents(otherPtr).x(); + collisionDist = std::min(collisionDist, relPos.length()); + + // Find the earliest `t` when |relPos + relSpeed * t| == collisionDist. + float vr = relPos.x() * relSpeed.x() + relPos.y() * relSpeed.y(); + float v2 = relSpeed.length2(); + float Dh = vr * vr - v2 * (relPos.length2() - collisionDist * collisionDist); + if (Dh <= 0 || v2 == 0) + continue; // No solution; distance is always >= collisionDist. + float t = (-vr - std::sqrt(Dh)) / v2; + + if (t < 0 || t > timeToCollision) + continue; + + // Check visibility and awareness last as it's expensive. + if (!MWBase::Environment::get().getWorld()->getLOS(otherPtr, ptr)) + continue; + if (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(otherPtr, ptr)) + continue; + + timeToCollision = t; + angleToApproachingActor = std::atan2(deltaPos.x(), deltaPos.y()); + osg::Vec2f posAtT = relPos + relSpeed * t; + float coef = (posAtT.x() * relSpeed.x() + posAtT.y() * relSpeed.y()) / (collisionDist * maxSpeed); + movementCorrection = posAtT * coef; + // Step to the side rather than backward. Otherwise player will be able to push the NPC far away from it's original location. + movementCorrection.y() = std::max(0.f, movementCorrection.y()); + } + + if (timeToCollision < maxTimeToCheck) + { + // Try to evade the nearest collision. + osg::Vec2f newMovement = origMovement + movementCorrection; + if (isMoving) + { // Keep the original speed. + newMovement.normalize(); + newMovement *= origMovement.length(); + } + movement.mPosition[0] = newMovement.x(); + movement.mPosition[1] = newMovement.y(); + if (shouldTurnToApproachingActor) + zTurn(ptr, angleToApproachingActor); + } + } + } + void Actors::update (float duration, bool paused) { if(!paused) @@ -1838,6 +1965,10 @@ namespace MWMechanics } } + static const bool avoidCollisions = Settings::Manager::getBool("NPCs avoid collisions", "Game"); + if (avoidCollisions) + predictAndAvoidCollisions(); + timerUpdateAITargets += duration; timerUpdateHeadTrack += duration; timerUpdateEquippedLight += duration; diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index bd5a14c0d..2668a7131 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -63,6 +63,8 @@ namespace MWMechanics void purgeSpellEffects (int casterActorId); + void predictAndAvoidCollisions(); + public: Actors(); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index cef97fa9b..375209a25 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -27,7 +27,7 @@ namespace MWMechanics { static const int COUNT_BEFORE_RESET = 10; - static const float DOOR_CHECK_INTERVAL = 1.5f; + static const float IDLE_POSITION_CHECK_INTERVAL = 1.5f; // to prevent overcrowding static const int DESTINATION_TOLERANCE = 64; @@ -425,15 +425,14 @@ namespace MWMechanics void AiWander::onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage) { - // Check if an idle actor is too close to a door - if so start walking - storage.mDoorCheckDuration += duration; + // Check if an idle actor is too far from all allowed nodes or too close to a door - if so start walking. + storage.mCheckIdlePositionTimer += duration; - if (storage.mDoorCheckDuration >= DOOR_CHECK_INTERVAL) + if (storage.mCheckIdlePositionTimer >= IDLE_POSITION_CHECK_INTERVAL && !isStationary()) { - storage.mDoorCheckDuration = 0; // restart timer - static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); - if (mDistance && // actor is not intended to be stationary - proximityToDoor(actor, distance*1.6f)) + storage.mCheckIdlePositionTimer = 0; // restart timer + static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance() * 1.6f; + if (proximityToDoor(actor, distance) || !isNearAllowedNode(actor, storage, distance)) { storage.setState(AiWanderStorage::Wander_MoveNow); storage.mTrimCurrentNode = false; // just in case @@ -452,6 +451,20 @@ namespace MWMechanics } } + bool AiWander::isNearAllowedNode(const MWWorld::Ptr& actor, const AiWanderStorage& storage, float distance) const + { + const osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3(); + auto cell = actor.getCell()->getCell(); + for (const ESM::Pathgrid::Point& node : storage.mAllowedNodes) + { + osg::Vec3f point(node.mX, node.mY, node.mZ); + Misc::CoordinateConverter(cell).toWorld(point); + if ((actorPos - point).length2() < distance * distance) + return true; + } + return false; + } + void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage) { // Is there no destination or are we there yet? diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 4138c3dea..4165cebbd 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -53,7 +53,7 @@ namespace MWMechanics ESM::Pathgrid::Point mCurrentNode; bool mTrimCurrentNode; - float mDoorCheckDuration; + float mCheckIdlePositionTimer; int mStuckCount; AiWanderStorage(): @@ -66,7 +66,7 @@ namespace MWMechanics mPopulateAvailableNodes(true), mAllowedNodes(), mTrimCurrentNode(false), - mDoorCheckDuration(0), // TODO: maybe no longer needed + mCheckIdlePositionTimer(0), mStuckCount(0) {}; @@ -117,6 +117,8 @@ namespace MWMechanics return mDestination; } + bool isStationary() const { return mDistance == 0; } + private: void stopWalking(const MWWorld::Ptr& actor); @@ -137,6 +139,7 @@ namespace MWMechanics void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance); bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); + bool isNearAllowedNode(const MWWorld::Ptr &actor, const AiWanderStorage& storage, float distance) const; const int mDistance; // how far the actor can wander from the spawn point const int mDuration; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 63df884e5..fab5fe569 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -328,6 +328,12 @@ turn to movement direction = false # Makes all movements of NPCs and player more smooth. smooth movement = false +# All actors avoid collisions with other actors. +NPCs avoid collisions = false + +# Give way to moving actors when idle. Requires 'NPCs avoid collisions' to be enabled. +NPCs give way = true + # Makes player swim a bit upward from the line of sight. swim upward correction = false From ad51a4be2e71f49b5b24970f2a5fa6fa1e1030f8 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Tue, 15 Sep 2020 23:25:32 +0200 Subject: [PATCH 43/50] Changes in head tracking and greetings for "smooth movement". --- apps/openmw/mwmechanics/actors.cpp | 19 +++++++++---------- apps/openmw/mwmechanics/actors.hpp | 1 + apps/openmw/mwmechanics/character.cpp | 22 +++++++++++++--------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 02b71e3d5..3de1a3acc 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -426,7 +426,7 @@ namespace MWMechanics const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3()); float sqrDist = (actor1Pos - actor2Pos).length2(); - if (sqrDist > maxDistance*maxDistance) + if (sqrDist > std::min(maxDistance * maxDistance, sqrHeadTrackDistance)) return; // stop tracking when target is behind the actor @@ -434,10 +434,7 @@ namespace MWMechanics osg::Vec3f targetDirection(actor2Pos - actor1Pos); actorDirection.z() = 0; targetDirection.z() = 0; - actorDirection.normalize(); - targetDirection.normalize(); - if (std::acos(actorDirection * targetDirection) < osg::DegreesToRadians(90.f) - && sqrDist <= sqrHeadTrackDistance + if (actorDirection * targetDirection > 0 && MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor)) { @@ -475,8 +472,7 @@ namespace MWMechanics void Actors::updateMovementSpeed(const MWWorld::Ptr& actor) { - static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game"); - if (smoothMovement) + if (mSmoothMovement) return; CreatureStats &stats = actor.getClass().getCreatureStats(actor); @@ -594,8 +590,11 @@ namespace MWMechanics if (!actorState.isTurningToPlayer()) { - actorState.setAngleToPlayer(std::atan2(dir.x(), dir.y())); - actorState.setTurningToPlayer(true); + float angle = std::atan2(dir.x(), dir.y()); + actorState.setAngleToPlayer(angle); + float deltaAngle = Misc::normalizeAngle(angle - actor.getRefData().getPosition().rot[2]); + if (!mSmoothMovement || std::abs(deltaAngle) > osg::DegreesToRadians(60.f)) + actorState.setTurningToPlayer(true); } } @@ -1467,7 +1466,7 @@ namespace MWMechanics } } - Actors::Actors() + Actors::Actors() : mSmoothMovement(Settings::Manager::getBool("smooth movement", "Game")) { mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 2668a7131..9299d468c 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -211,6 +211,7 @@ namespace MWMechanics float mTimerDisposeSummonsCorpses; float mActorsProcessingRange; + bool mSmoothMovement; }; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f7d745250..1dcccb888 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2038,6 +2038,8 @@ void CharacterController::update(float duration, bool animationOnly) mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 2); else mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4); + if (smoothMovement && !isPlayer && !inwater) + mAnimation->setUpperBodyYawRadians(mAnimation->getUpperBodyYawRadians() + mAnimation->getHeadYaw() / 2); speed = cls.getCurrentSpeed(mPtr); vec.x() *= speed; @@ -2934,19 +2936,21 @@ void CharacterController::updateHeadTracking(float duration) return; const osg::Vec3f actorDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0); - zAngleRadians = std::atan2(direction.x(), direction.y()) - std::atan2(actorDirection.x(), actorDirection.y()); - xAngleRadians = -std::asin(direction.z()); - - const double xLimit = osg::DegreesToRadians(40.0); - const double zLimit = osg::DegreesToRadians(30.0); - zAngleRadians = osg::clampBetween(Misc::normalizeAngle(zAngleRadians), -xLimit, xLimit); - xAngleRadians = osg::clampBetween(Misc::normalizeAngle(xAngleRadians), -zLimit, zLimit); + zAngleRadians = std::atan2(actorDirection.x(), actorDirection.y()) - std::atan2(direction.x(), direction.y()); + xAngleRadians = std::asin(direction.z()); } + const double xLimit = osg::DegreesToRadians(40.0); + const double zLimit = osg::DegreesToRadians(30.0); + double zLimitOffset = mAnimation->getUpperBodyYawRadians(); + xAngleRadians = osg::clampBetween(Misc::normalizeAngle(xAngleRadians), -xLimit, xLimit); + zAngleRadians = osg::clampBetween(Misc::normalizeAngle(zAngleRadians), + -zLimit + zLimitOffset, zLimit + zLimitOffset); + float factor = duration*5; factor = std::min(factor, 1.f); - xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * (-xAngleRadians); - zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * (-zAngleRadians); + xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * xAngleRadians; + zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * zAngleRadians; mAnimation->setHeadPitch(xAngleRadians); mAnimation->setHeadYaw(zAngleRadians); From a17fb14b8a94f65338572f2712ad711a7de17299 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Tue, 15 Sep 2020 20:28:42 +0200 Subject: [PATCH 44/50] Add "smooth movement" and "NPCs avoid collisions" settings to openmw-launcher and to the docs; update CHANGELOG.md. --- CHANGELOG.md | 2 + apps/launcher/advancedpage.cpp | 4 ++ .../reference/modding/settings/game.rst | 37 ++++++++++++++++++- files/ui/advancedpage.ui | 22 ++++++++++- 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfb6532f3..7c8c6cc00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ Bug #5557: Diagonal movement is noticeably slower with analogue stick Feature #390: 3rd person look "over the shoulder" Feature #2386: Distant Statics in the form of Object Paging + Feature #4894: Consider actors as obstacles for pathfinding Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines @@ -58,6 +59,7 @@ Feature #5524: Resume failed script execution after reload Feature #5525: Search fields tweaks (utf-8) Feature #5545: Option to allow stealing from an unconscious NPC during combat + Feature #5610: Actors movement should be smoother Task #5480: Drop Qt4 support Task #5520: Improve cell name autocompleter implementation diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index bc14a9269..2e929faf5 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -88,6 +88,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game"); + loadSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game"); int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game"); if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); @@ -112,6 +113,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); } loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); + loadSettingBool(smoothMovementCheckBox, "smooth movement", "Game"); const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain"); const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain"); @@ -200,6 +202,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game"); + saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game"); int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex(); if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); @@ -220,6 +223,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); + saveSettingBool(smoothMovementCheckBox, "smooth movement", "Game"); const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain"); const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain"); diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 46bd22e50..f942f6e7c 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -331,8 +331,43 @@ If enabled then the character turns lower body to the direction of movement. Upp This setting can be controlled in Advanced tab of the launcher. +smooth movement +--------------- + +:Type: boolean +:Range: True/False +:Default: False + +Makes NPCs and player movement more smooth. + +Recommended to use with "turn to movement direction" enabled. + +This setting can be controlled in Advanced tab of the launcher. + +NPCs avoid collisions +--------------------- + +:Type: boolean +:Range: True/False +:Default: False + +If enabled NPCs apply evasion maneuver to avoid collisions with others. + +This setting can be controlled in Advanced tab of the launcher. + +NPCs give way +------------- + +:Type: boolean +:Range: True/False +:Default: True + +Standing NPCs give way to moving ones. Works only if 'NPCs avoid collisions' is enabled. + +This setting can only be configured by editing the settings configuration file. + swim upward correction ----------------- +---------------------- :Type: boolean :Range: True/False diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 9084a7aba..3a91db791 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -43,6 +43,16 @@ + + + + <html><head/><body><p>If enabled NPCs apply evasion maneuver to avoid collisions with others.</p></body></html> + + + NPCs avoid collisions + + + @@ -233,6 +243,16 @@ + + + + <html><head/><body><p>Makes NPCs and player movement more smooth. Recommended to use with "turn to movement direction" enabled.</p></body></html> + + + Smooth movement + + + @@ -283,7 +303,7 @@ - + <html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html> From a338e8c56102b9cfb1b1213268b81ed6c64a6c00 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 24 Sep 2020 17:13:09 +0100 Subject: [PATCH 45/50] Actually set the uvSet value This got lost when uvSet was made into a local variable. --- components/nifosg/nifloader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 24184f2c8..f9274cebf 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1507,6 +1507,8 @@ namespace NifOsg texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + + uvSet = tex.uvSet; } else { From e0a5c24afe230e45c40ca3a64957de0318ca788a Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Mon, 21 Sep 2020 23:05:26 +0300 Subject: [PATCH 46/50] Handle 0-use items like vanilla (bug #5611) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/repair.cpp | 3 ++- apps/openmw/mwmechanics/security.cpp | 18 +++++++++++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfb6532f3..7259bd1b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ Bug #5539: Window resize breaks when going from a lower resolution to full screen resolution Bug #5548: Certain exhausted topics can be highlighted again even though there's no new dialogue Bug #5557: Diagonal movement is noticeably slower with analogue stick + Bug #5611: Usable items with "0 Uses" should be used only once Feature #390: 3rd person look "over the shoulder" Feature #2386: Distant Statics in the form of Object Paging Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 389d00d85..81886ed9b 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -27,7 +27,8 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) // reduce number of uses left int uses = mTool.getClass().getItemHealth(mTool); - mTool.getCellRef().setCharge(uses-1); + uses -= std::min(uses, 1); + mTool.getCellRef().setCharge(uses); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index 001375feb..5b79d821c 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -33,6 +33,10 @@ namespace MWMechanics !lock.getClass().hasToolTip(lock)) //If it's unlocked or can not be unlocked back out immediately return; + int uses = lockpick.getClass().getItemHealth(lockpick); + if (uses == 0) + return; + int lockStrength = lock.getCellRef().getLockLevel(); float pickQuality = lockpick.get()->mBase->mData.mQuality; @@ -61,9 +65,7 @@ namespace MWMechanics resultMessage = "#{sLockFail}"; } - int uses = lockpick.getClass().getItemHealth(lockpick); - --uses; - lockpick.getCellRef().setCharge(uses); + lockpick.getCellRef().setCharge(uses-1); if (!uses) lockpick.getContainerStore()->remove(lockpick, 1, mActor); } @@ -71,7 +73,11 @@ namespace MWMechanics void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe, std::string& resultMessage, std::string& resultSound) { - if (trap.getCellRef().getTrap() == "") + if (trap.getCellRef().getTrap().empty()) + return; + + int uses = probe.getClass().getItemHealth(probe); + if (uses == 0) return; float probeQuality = probe.get()->mBase->mData.mQuality; @@ -104,9 +110,7 @@ namespace MWMechanics resultMessage = "#{sTrapFail}"; } - int uses = probe.getClass().getItemHealth(probe); - --uses; - probe.getCellRef().setCharge(uses); + probe.getCellRef().setCharge(uses-1); if (!uses) probe.getContainerStore()->remove(probe, 1, mActor); } From f2af957647a83cc1d7a35db508f3bb2d251d77fd Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Fri, 25 Sep 2020 02:12:36 +0300 Subject: [PATCH 47/50] Reset enchant effects displayed range when they are constant (bug #5603) --- CHANGELOG.md | 1 + apps/openmw/mwgui/spellcreationdialog.cpp | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfb6532f3..74d0e048d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ Bug #5539: Window resize breaks when going from a lower resolution to full screen resolution Bug #5548: Certain exhausted topics can be highlighted again even though there's no new dialogue Bug #5557: Diagonal movement is noticeably slower with analogue stick + Bug #5603: Setting constant effect cast style doesn't correct effects view Feature #390: 3rd person look "over the shoulder" Feature #2386: Distant Statics in the form of Object Paging Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index bfdba8b2e..1f086507f 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -739,5 +739,24 @@ namespace MWGui { mAddEffectDialog.setConstantEffect(constant); mConstantEffect = constant; + + if (!constant) + return; + + for (auto it = mEffects.begin(); it != mEffects.end();) + { + if (it->mRange != ESM::RT_Self) + { + auto& store = MWBase::Environment::get().getWorld()->getStore(); + auto magicEffect = store.get().find(it->mEffectID); + if ((magicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) == 0) + { + it = mEffects.erase(it); + continue; + } + it->mRange = ESM::RT_Self; + } + ++it; + } } } From bf7e1bd32bc1d1e29d66c9bdf2eae19022b14f21 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 29 Sep 2020 12:21:25 +0200 Subject: [PATCH 48/50] make switch to using GL hosted windows deps --- CI/before_script.msvc.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index e43f5623f..e00e582d4 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -523,52 +523,52 @@ if [ -z $SKIP_DOWNLOAD ]; then # Boost if [ -z $APPVEYOR ]; then download "Boost ${BOOST_VER}" \ - "https://sourceforge.net/projects/boost/files/boost-binaries/${BOOST_VER}/boost_${BOOST_VER_URL}-msvc-${MSVC_VER}-${BITS}.exe" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/${BOOST_VER}/boost_${BOOST_VER_URL}-msvc-${MSVC_VER}-${BITS}.exe" \ "boost-${BOOST_VER}-msvc${MSVC_VER}-win${BITS}.exe" fi # Bullet download "Bullet 2.89 (${BULLET_DBL_DISPLAY})" \ - "https://rgw.ctrl-c.liu.se/openmw/Deps/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \ "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" # FFmpeg download "FFmpeg 4.2.2" \ - "https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-4.2.2-win${BITS}-shared.zip" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/ffmpeg-4.2.2-win${BITS}.zip" \ "ffmpeg-4.2.2-win${BITS}.zip" \ - "https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-4.2.2-win${BITS}-dev.zip" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/ffmpeg-4.2.2-win${BITS}-dev.zip" \ "ffmpeg-4.2.2-dev-win${BITS}.zip" # MyGUI download "MyGUI 3.4.0" \ - "https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \ "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" if [ -n "$PDBS" ]; then download "MyGUI symbols" \ - "https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \ "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" fi # OpenAL download "OpenAL-Soft 1.20.1" \ - "http://openal-soft.org/openal-binaries/openal-soft-1.20.1-bin.zip" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/openal-soft-1.20.1-bin.zip" \ "OpenAL-Soft-1.20.1.zip" # OSG download "OpenSceneGraph 3.6.5" \ - "https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \ "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" if [ -n "$PDBS" ]; then download "OpenSceneGraph symbols" \ - "https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \ "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" fi # SDL2 download "SDL 2.0.12" \ - "https://www.libsdl.org/release/SDL2-devel-2.0.12-VC.zip" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/SDL2-devel-2.0.12-VC.zip" \ "SDL2-2.0.12.zip" # Google test and mock From 18899394c478f186f343830fa7bba16c6a275873 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 29 Sep 2020 13:13:26 +0200 Subject: [PATCH 49/50] typo in path --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index e00e582d4..ee4cd839b 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -536,7 +536,7 @@ if [ -z $SKIP_DOWNLOAD ]; then download "FFmpeg 4.2.2" \ "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/ffmpeg-4.2.2-win${BITS}.zip" \ "ffmpeg-4.2.2-win${BITS}.zip" \ - "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/ffmpeg-4.2.2-win${BITS}-dev.zip" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/ffmpeg-4.2.2-dev-win${BITS}.zip" \ "ffmpeg-4.2.2-dev-win${BITS}.zip" # MyGUI From c291bb169efea2a20fdc2e305c75679acd474818 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 29 Sep 2020 13:29:12 +0200 Subject: [PATCH 50/50] fixed indentation and additional diffs --- CI/before_script.msvc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index ee4cd839b..e8d65372d 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -552,7 +552,7 @@ if [ -z $SKIP_DOWNLOAD ]; then # OpenAL download "OpenAL-Soft 1.20.1" \ - "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/openal-soft-1.20.1-bin.zip" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OpenAL-Soft-1.20.1.zip" \ "OpenAL-Soft-1.20.1.zip" # OSG @@ -568,7 +568,7 @@ if [ -z $SKIP_DOWNLOAD ]; then # SDL2 download "SDL 2.0.12" \ - "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/SDL2-devel-2.0.12-VC.zip" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/SDL2-2.0.12.zip" \ "SDL2-2.0.12.zip" # Google test and mock