From d170a50e8a8f6086c858a8e0a55ee43f5e5dfdbc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 1 Aug 2016 11:18:35 +0200 Subject: [PATCH 01/19] increased version number --- CMakeLists.txt | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa6d76a4f..124193a0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ endif() message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 39) +set(OPENMW_VERSION_MINOR 40) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") diff --git a/README.md b/README.md index b1db74549..a8e6c0c0e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ OpenMW is a recreation of the engine for the popular role-playing game Morrowind OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set. -* Version: 0.39.0 +* Version: 0.40.0 * License: GPL (see docs/license/GPL3.txt for more information) * Website: http://www.openmw.org * IRC: #openmw on irc.freenode.net From b8b2f087cd3e5f6a14fcda2db346e57b0251596c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 4 Aug 2016 15:00:07 +0200 Subject: [PATCH 02/19] updated changelog --- CHANGELOG.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d427fa82..aa7908d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,63 @@ +0.40.0 +------ + + Bug #1320: AiWander - Creatures in cells without pathgrids do not wander + Bug #1873: Death events are triggered at the beginning of the death animation + Bug #1996: Resting interrupts magic effects + Bug #2399: Vampires can rest in broad daylight and survive the experience + Bug #2981: When waiting, NPCs can go where they wouldn't go normally. + Bug #3045: Esp files containing the letter '#' in the file name cannot be loaded on startup + Bug #3071: Slowfall does not stop momentum when jumping + Bug #3085: Plugins can not replace parent cell references with a cell reference of different type + Bug #3145: Bug with AI Cliff Racer. He will not attack you, unless you put in front of him. + Bug #3149: Editor: Weather tables were missing from regions + Bug #3201: Netch shoots over your head + Bug #3269: If you deselect a mod and try to load a save made inside a cell added by it, you end bellow the terrain in the grid 0/0 + Bug #3286: Editor: Script editor tab width + Bug #3329: Teleportation spells cause crash to desktop after build update from 0.37 to 0.38.0 + Bug #3331: Editor: Start Scripts table: Adding a script doesn't refresh the list of Start Scripts and allows to add a single script multiple times + Bug #3332: Editor: Scene view: Tool tips only occur when holding the left mouse button + Bug #3340: ESS-Importer does not separate item stacks + Bug #3342: Editor: Creation of pathgrids did not check if the pathgrid already existed + Bug #3346: "Talked to PC" is always 0 for "Hello" dialogue + Bug #3349: AITravel doesn't repeat + Bug #3370: NPCs wandering to invalid locations after training + Bug #3378: "StopCombat" command does not function in vanilla quest + Bug #3384: Battle at Nchurdamz - Larienna Macrina does not stop combat after killing Hrelvesuu + Bug #3388: Monster Respawn tied to Quicksave + Bug #3390: Strange visual effect in Dagoth Ur's chamber + Bug #3391: Inappropriate Blight weather behavior at end of main quest + Bug #3394: Replaced dialogue inherits some of its old data + Bug #3397: Actors that start the game dead always have the same death pose + Bug #3401: Sirollus Saccus sells not glass arrows + Bug #3402: Editor: Weapon data not being properly set + Bug #3407: Tanisie Verethi will immediately detect the player + Bug #3408: Improper behavior of ashmire particles + Bug #3416: 1st person and 3rd person camera isn't converted from .ess correctly + Bug #3421: Idling long enough while paralyzed sometimes causes character to get stuck + Bug #3423: Sleep interruption inside dungeons too agressive + Bug #3424: Pickpocketing sometimes won't work + Bug #3434: Dead NPC's and Creatures still contribute to sneak skill increases + Bug #3437: Weather-conditioned dialogue should not play in interiors + Bug #3439: Effects cast by summon stick around after their death + Bug #3440: Parallax maps looks weird + Bug #3443: Class graphic for custom class should be Acrobat + Bug #3446: OpenMW segfaults when using Atrayonis's "Anthology Solstheim: Tomb of the Snow Prince" mod + Bug #3448: After dispelled, invisibility icon is still displayed + Bug #3453: First couple of seconds of NPC speech is muted + Bug #3455: Portable house mods lock player and npc movement up exiting house. + Bug #3456: Equipping an item will undo dispel of constant effect invisibility + Bug #3458: Constant effect restore health doesn't work during Wait + Bug #3466: It is possible to stack multiple scroll effects of the same type + Bug #3471: When two mods delete the same references, many references are not disabled by the engine. + Bug #3473: 3rd person camera can be glitched + Feature #1424: NPC "Face" function + Feature #2974: Editor: Multiple Deletion of Subrecords + Feature #3044: Editor: Render path grid v2 + Feature #3362: Editor: Configurable key bindings + Feature #3375: Make sun / moon reflections weather dependent + Feature #3386: Editor: Edit pathgrid + 0.39.0 ------ From 1d35fc4b21afdc62c1478d99d67af4ee9b8bf190 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 5 Aug 2016 09:08:53 +0200 Subject: [PATCH 03/19] updated change log --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa7908d1f..dc823cb3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Bug #1873: Death events are triggered at the beginning of the death animation Bug #1996: Resting interrupts magic effects Bug #2399: Vampires can rest in broad daylight and survive the experience + Bug #2604: Incorrect magicka recalculation + Bug #2721: Telekinesis extends interaction range where it shouldn't Bug #2981: When waiting, NPCs can go where they wouldn't go normally. Bug #3045: Esp files containing the letter '#' in the file name cannot be loaded on startup Bug #3071: Slowfall does not stop momentum when jumping @@ -31,8 +33,10 @@ Bug #3397: Actors that start the game dead always have the same death pose Bug #3401: Sirollus Saccus sells not glass arrows Bug #3402: Editor: Weapon data not being properly set + Bug #3405: Mulvisic Othril will not use her chitin throwing stars Bug #3407: Tanisie Verethi will immediately detect the player Bug #3408: Improper behavior of ashmire particles + Bug #3412: Ai Wander start time resets when saving/loading the game Bug #3416: 1st person and 3rd person camera isn't converted from .ess correctly Bug #3421: Idling long enough while paralyzed sometimes causes character to get stuck Bug #3423: Sleep interruption inside dungeons too agressive From c4b3727d57372788f2462289f22e87947ae5d062 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 8 Aug 2016 14:36:50 -0400 Subject: [PATCH 04/19] Fix pointer being deleted twice. --- apps/opencs/view/render/orbitcameramode.cpp | 4 ++-- apps/opencs/view/render/orbitcameramode.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/orbitcameramode.cpp b/apps/opencs/view/render/orbitcameramode.cpp index c7d980454..ba25beaba 100644 --- a/apps/opencs/view/render/orbitcameramode.cpp +++ b/apps/opencs/view/render/orbitcameramode.cpp @@ -15,9 +15,9 @@ namespace CSVRender , mWorldspaceWidget(worldspaceWidget) , mCenterOnSelection(0) { - mCenterShortcut.reset(new CSMPrefs::Shortcut("orbit-center-selection", worldspaceWidget)); + mCenterShortcut = new CSMPrefs::Shortcut("orbit-center-selection", worldspaceWidget); mCenterShortcut->enable(false); - connect(mCenterShortcut.get(), SIGNAL(activated()), this, SLOT(centerSelection())); + connect(mCenterShortcut, SIGNAL(activated()), this, SLOT(centerSelection())); } OrbitCameraMode::~OrbitCameraMode() diff --git a/apps/opencs/view/render/orbitcameramode.hpp b/apps/opencs/view/render/orbitcameramode.hpp index 4f72de957..312cd1756 100644 --- a/apps/opencs/view/render/orbitcameramode.hpp +++ b/apps/opencs/view/render/orbitcameramode.hpp @@ -32,7 +32,7 @@ namespace CSVRender WorldspaceWidget* mWorldspaceWidget; QAction* mCenterOnSelection; - std::auto_ptr mCenterShortcut; + CSMPrefs::Shortcut* mCenterShortcut; private slots: From c9ffd61491e7ceb3bda35054e5339f21602939d1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 10 Aug 2016 10:18:00 +0200 Subject: [PATCH 05/19] updated credits file --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 60e969cdc..258c6dce8 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -99,6 +99,7 @@ Programmers Nikolay Kasyanov (corristo) nobrakal Nolan Poe (nopoe) + Oleg Chkan (mrcheko) Paul Cercueil (pcercuei) Paul McElroy (Greendogo) Pi03k From a632974998480bf949dc689c88dd82bc73fc5898 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 15 Aug 2016 11:27:54 +0200 Subject: [PATCH 06/19] updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc823cb3f..0add2e02e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Bug #3421: Idling long enough while paralyzed sometimes causes character to get stuck Bug #3423: Sleep interruption inside dungeons too agressive Bug #3424: Pickpocketing sometimes won't work + Bug #3432: AiFollow / AiEscort durations handled incorrectly Bug #3434: Dead NPC's and Creatures still contribute to sneak skill increases Bug #3437: Weather-conditioned dialogue should not play in interiors Bug #3439: Effects cast by summon stick around after their death From b332a13b4e543adc95c5f8b9b99a963c7d111d1a Mon Sep 17 00:00:00 2001 From: Allofich Date: Wed, 17 Aug 2016 22:48:55 +0900 Subject: [PATCH 07/19] Don't restart looped animations on repeated calls --- apps/openmw/mwmechanics/character.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c92530259..711feb4a6 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2009,6 +2009,9 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int } else { + if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname && isAnimPlaying(mAnimQueue.front().mGroup)) + return true; + count = std::max(count, 1); AnimationQueueEntry entry; From 91fd96614658b3661bebbe6c1b41e52e989fbeeb Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Thu, 18 Aug 2016 12:42:35 -0400 Subject: [PATCH 08/19] Fix some issues detected by coverity for the editor. --- apps/opencs/model/prefs/modifiersetting.cpp | 1 + apps/opencs/model/prefs/shortcutsetting.cpp | 1 + apps/opencs/view/render/instancemode.cpp | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/prefs/modifiersetting.cpp b/apps/opencs/model/prefs/modifiersetting.cpp index d8bf84342..4f4fac248 100644 --- a/apps/opencs/model/prefs/modifiersetting.cpp +++ b/apps/opencs/model/prefs/modifiersetting.cpp @@ -15,6 +15,7 @@ namespace CSMPrefs ModifierSetting::ModifierSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key, const std::string& label) : Setting(parent, values, mutex, key, label) + , mButton(0) , mEditorActive(false) { } diff --git a/apps/opencs/model/prefs/shortcutsetting.cpp b/apps/opencs/model/prefs/shortcutsetting.cpp index 726566fdd..c56119deb 100644 --- a/apps/opencs/model/prefs/shortcutsetting.cpp +++ b/apps/opencs/model/prefs/shortcutsetting.cpp @@ -17,6 +17,7 @@ namespace CSMPrefs ShortcutSetting::ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key, const std::string& label) : Setting(parent, values, mutex, key, label) + , mButton(0) , mEditorActive(false) , mEditorPos(0) { diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 394e6772d..f5b15cb55 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -74,7 +74,9 @@ osg::Vec3f CSVRender::InstanceMode::getSelectionCenter(const std::vector 0) + center /= objectCount; return center; } @@ -92,7 +94,7 @@ osg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos) CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":placeholder"), Mask_Reference, "Instance editing", parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None), - mDragAxis (-1), mLocked (false) + mDragAxis (-1), mLocked (false), mUnitScaleDist(1) { } From 416466e53e21f281530b6f20f8de1331a3054e66 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 19 Aug 2016 19:53:32 +0300 Subject: [PATCH 09/19] Fix uninitialized escape_hash_filter --- components/files/escape.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/files/escape.cpp b/components/files/escape.cpp index 9a795b95f..c5d2c041e 100644 --- a/components/files/escape.cpp +++ b/components/files/escape.cpp @@ -9,7 +9,7 @@ namespace Files const int escape_hash_filter::sEscapeIdentifier = 'a'; const int escape_hash_filter::sHashIdentifier = 'h'; - escape_hash_filter::escape_hash_filter() : mNext(), mSeenNonWhitespace(false), mFinishLine(false) + escape_hash_filter::escape_hash_filter() : mNext(), mPrevious(), mSeenNonWhitespace(false), mFinishLine(false) { } @@ -137,4 +137,4 @@ namespace Files return istream; } -} \ No newline at end of file +} From 8c5aae0722c56395f59814ea5029bbaa81f68625 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 21 Aug 2016 18:18:41 +0900 Subject: [PATCH 10/19] Add virtual destructors --- components/compiler/lineparser.cpp | 2 ++ components/compiler/lineparser.hpp | 3 +++ components/misc/messageformatparser.cpp | 2 ++ components/misc/messageformatparser.hpp | 2 ++ 4 files changed, 9 insertions(+) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index a5b67f58b..ad79877ef 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -59,6 +59,8 @@ namespace Compiler mExprParser (errorHandler, context, locals, literals), mAllowExpression (allowExpression) {} + LineParser::~LineParser() {} + bool LineParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) { if (mAllowExpression && mState==BeginState) diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index d92c4895e..1de4fa927 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -51,6 +51,9 @@ namespace Compiler bool allowExpression = false); ///< \param allowExpression Allow lines consisting of a naked expression /// (result is send to the messagebox interface) + + virtual ~LineParser(); + ///< destructor virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. diff --git a/components/misc/messageformatparser.cpp b/components/misc/messageformatparser.cpp index 7c0c978b2..bfd8dd562 100644 --- a/components/misc/messageformatparser.cpp +++ b/components/misc/messageformatparser.cpp @@ -2,6 +2,8 @@ namespace Misc { + MessageFormatParser::~MessageFormatParser() {} + void MessageFormatParser::process(const std::string& m) { for (unsigned int i = 0; i < m.size(); ++i) diff --git a/components/misc/messageformatparser.hpp b/components/misc/messageformatparser.hpp index 48faff714..c12b9352a 100644 --- a/components/misc/messageformatparser.hpp +++ b/components/misc/messageformatparser.hpp @@ -19,6 +19,8 @@ namespace Misc virtual void visitedCharacter(char c) = 0; public: + virtual ~MessageFormatParser(); + virtual void process(const std::string& message); }; } From 73463cd12f494171c4e9bb039c3962c7b77f029a Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 22 Aug 2016 20:21:47 +0900 Subject: [PATCH 11/19] Remove unnecessary virtual destructor --- components/compiler/lineparser.cpp | 2 -- components/compiler/lineparser.hpp | 3 --- 2 files changed, 5 deletions(-) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index ad79877ef..a5b67f58b 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -59,8 +59,6 @@ namespace Compiler mExprParser (errorHandler, context, locals, literals), mAllowExpression (allowExpression) {} - LineParser::~LineParser() {} - bool LineParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) { if (mAllowExpression && mState==BeginState) diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index 1de4fa927..d92c4895e 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -51,9 +51,6 @@ namespace Compiler bool allowExpression = false); ///< \param allowExpression Allow lines consisting of a naked expression /// (result is send to the messagebox interface) - - virtual ~LineParser(); - ///< destructor virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. From bf9dc45b2bafb39994789239c22c61a980b00b3e Mon Sep 17 00:00:00 2001 From: Allofich Date: Thu, 18 Aug 2016 01:08:54 +0900 Subject: [PATCH 12/19] Emulate vanilla animation loops more closely --- apps/openmw/mwmechanics/character.cpp | 27 +++++++-- apps/openmw/mwrender/animation.cpp | 87 ++++++++++++++++----------- apps/openmw/mwrender/animation.hpp | 9 ++- 3 files changed, 83 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 711feb4a6..5310dfdbb 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1533,6 +1533,9 @@ void CharacterController::update(float duration) updateMagicEffects(); + if(mAnimQueue.size() > 1 && (mAnimation->getLoopingEnabled(mAnimQueue.front().mGroup) == true)) + mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, false); + if(!cls.isActor()) { if(mAnimQueue.size() > 1) @@ -2009,8 +2012,26 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int } else { - if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname && isAnimPlaying(mAnimQueue.front().mGroup)) - return true; + // If the given animation is a looped animation, is already playing + // and has not yet reached its Loop Stop key, make it the only animation + // in the queue, and retain the loop count from the animation that was + // already playing. This emulates observed behavior from the original + // engine and allows banners to animate correctly. + if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname && + mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop start") >= 0) + { + float endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop stop"); + + if (endOfLoop < 0) // if no Loop Stop key was found, use the Stop key + endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": stop"); + + if (endOfLoop > 0 && (mAnimation->getCurrentTime(mAnimQueue.front().mGroup) < endOfLoop)) + { + mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, true); + mAnimQueue.resize(1); + return true; + } + } count = std::max(count, 1); @@ -2035,8 +2056,6 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int } else if(mode == 0) { - if (!mAnimQueue.empty()) - mAnimation->stopLooping(mAnimQueue.front().mGroup); mAnimQueue.resize(1); mAnimQueue.push_back(entry); } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e5614f3f8..03370e7f8 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1023,50 +1023,54 @@ namespace MWRender { float targetTime; - if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0) - goto handle_loop; - - targetTime = state.getTime() + timepassed; - if(textkey == textkeys.end() || textkey->first > targetTime) - { - if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) - updatePosition(state.getTime(), targetTime, movement); - state.setTime(std::min(targetTime, state.mStopTime)); - } - else + if (state.getTime() < state.mLoopStopTime || state.mLoopCount == 0) { - if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) - updatePosition(state.getTime(), textkey->first, movement); - state.setTime(textkey->first); - } - - state.mPlaying = (state.getTime() < state.mStopTime); - timepassed = targetTime - state.getTime(); - - while(textkey != textkeys.end() && textkey->first <= state.getTime()) - { - handleTextKey(state, stateiter->first, textkey, textkeys); - ++textkey; - } + targetTime = state.getTime() + timepassed; + if(textkey == textkeys.end() || textkey->first > targetTime) + { + if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) + updatePosition(state.getTime(), targetTime, movement); + state.setTime(std::min(targetTime, state.mStopTime)); + } + else + { + if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) + updatePosition(state.getTime(), textkey->first, movement); + state.setTime(textkey->first); + } - if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0) - { - handle_loop: - state.mLoopCount--; - state.setTime(state.mLoopStartTime); - state.mPlaying = true; + state.mPlaying = (state.getTime() < state.mStopTime); + timepassed = targetTime - state.getTime(); - textkey = textkeys.lower_bound(state.getTime()); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, stateiter->first, textkey, textkeys); ++textkey; } - - if(state.getTime() >= state.mLoopStopTime) - break; } + if(state.getTime() >= state.mLoopStopTime) + { + if (!state.mLoopingEnabled) + state.mLoopCount = 0; + else if (state.mLoopCount > 0) + { + state.mLoopCount--; + state.setTime(state.mLoopStartTime); + state.mPlaying = true; + + textkey = textkeys.lower_bound(state.getTime()); + while(textkey != textkeys.end() && textkey->first <= state.getTime()) + { + handleTextKey(state, stateiter->first, textkey, textkeys); + ++textkey; + } + + if(state.getTime() >= state.mLoopStopTime) + break; + } + } + if(timepassed <= 0.0f) break; } @@ -1095,6 +1099,21 @@ namespace MWRender return movement; } + void Animation::setLoopingEnabled(const std::string &groupname, bool enabled) + { + AnimStateMap::iterator state(mStates.find(groupname)); + if(state != mStates.end()) + state->second.mLoopingEnabled = enabled; + } + + bool Animation::getLoopingEnabled(const std::string &groupname) const + { + AnimStateMap::const_iterator state(mStates.find(groupname)); + if(state != mStates.end()) + return state->second.mLoopingEnabled; + return false; + } + void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature) { osg::ref_ptr previousStateset; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index a837a26ae..2eb857cbd 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -183,6 +183,7 @@ protected: float mSpeedMult; bool mPlaying; + bool mLoopingEnabled; size_t mLoopCount; AnimPriority mPriority; @@ -190,8 +191,8 @@ protected: bool mAutoDisable; AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f), - mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), - mPriority(0), mBlendMask(0), mAutoDisable(true) + mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopingEnabled(true), + mLoopCount(0), mPriority(0), mBlendMask(0), mAutoDisable(true) { } ~AnimState(); @@ -432,6 +433,10 @@ public: virtual osg::Vec3f runAnimation(float duration); + void setLoopingEnabled(const std::string &groupname, bool enabled); + + bool getLoopingEnabled(const std::string &groupname) const; + /// This is typically called as part of runAnimation, but may be called manually if needed. void updateEffects(float duration); From 6450c9be27a1a16683c658e93ecc47c80a4a7b7b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Aug 2016 22:57:56 +0200 Subject: [PATCH 13/19] Simplify condition --- apps/openmw/mwmechanics/character.cpp | 4 ++-- apps/openmw/mwrender/animation.cpp | 8 -------- apps/openmw/mwrender/animation.hpp | 2 -- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5310dfdbb..be321ce28 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1533,8 +1533,8 @@ void CharacterController::update(float duration) updateMagicEffects(); - if(mAnimQueue.size() > 1 && (mAnimation->getLoopingEnabled(mAnimQueue.front().mGroup) == true)) - mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, false); + if(!mAnimQueue.empty()) + mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, mAnimQueue.size() <= 1); if(!cls.isActor()) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 03370e7f8..a0b68fd6c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1106,14 +1106,6 @@ namespace MWRender state->second.mLoopingEnabled = enabled; } - bool Animation::getLoopingEnabled(const std::string &groupname) const - { - AnimStateMap::const_iterator state(mStates.find(groupname)); - if(state != mStates.end()) - return state->second.mLoopingEnabled; - return false; - } - void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature) { osg::ref_ptr previousStateset; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 2eb857cbd..4eeb5d943 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -435,8 +435,6 @@ public: void setLoopingEnabled(const std::string &groupname, bool enabled); - bool getLoopingEnabled(const std::string &groupname) const; - /// This is typically called as part of runAnimation, but may be called manually if needed. void updateEffects(float duration); From 0c9882956afe8ad20b9673af0adf2e1627fd571b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Aug 2016 22:58:24 +0200 Subject: [PATCH 14/19] Add AnimState::shouldLoop() --- apps/openmw/mwrender/animation.cpp | 32 ++++++++++++------------------ apps/openmw/mwrender/animation.hpp | 5 +++++ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a0b68fd6c..29407bc2d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1023,7 +1023,7 @@ namespace MWRender { float targetTime; - if (state.getTime() < state.mLoopStopTime || state.mLoopCount == 0) + if (!state.shouldLoop()) { targetTime = state.getTime() + timepassed; if(textkey == textkeys.end() || textkey->first > targetTime) @@ -1048,27 +1048,21 @@ namespace MWRender ++textkey; } } - - if(state.getTime() >= state.mLoopStopTime) + if(state.shouldLoop()) { - if (!state.mLoopingEnabled) - state.mLoopCount = 0; - else if (state.mLoopCount > 0) - { - state.mLoopCount--; - state.setTime(state.mLoopStartTime); - state.mPlaying = true; - - textkey = textkeys.lower_bound(state.getTime()); - while(textkey != textkeys.end() && textkey->first <= state.getTime()) - { - handleTextKey(state, stateiter->first, textkey, textkeys); - ++textkey; - } + state.mLoopCount--; + state.setTime(state.mLoopStartTime); + state.mPlaying = true; - if(state.getTime() >= state.mLoopStopTime) - break; + textkey = textkeys.lower_bound(state.getTime()); + while(textkey != textkeys.end() && textkey->first <= state.getTime()) + { + handleTextKey(state, stateiter->first, textkey, textkeys); + ++textkey; } + + if(state.getTime() >= state.mLoopStopTime) + break; } if(timepassed <= 0.0f) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 4eeb5d943..749890ae5 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -205,6 +205,11 @@ protected: { *mTime = time; } + + bool shouldLoop() const + { + return getTime() >= mLoopStopTime && mLoopingEnabled && mLoopCount > 0; + } }; typedef std::map AnimStateMap; AnimStateMap mStates; From 719e884b7c01d9416a957d17eb48183e87185424 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Aug 2016 23:02:57 +0200 Subject: [PATCH 15/19] Remove duplicate code --- apps/openmw/mwmechanics/character.cpp | 48 +++++++++++---------------- apps/openmw/mwmechanics/character.hpp | 2 ++ 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index be321ce28..873d3ce7e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1524,6 +1524,23 @@ bool CharacterController::updateWeaponState() return forcestateupdate; } +void CharacterController::updateAnimQueue() +{ + if(mAnimQueue.size() > 1) + { + if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false) + { + mAnimation->disable(mAnimQueue.front().mGroup); + mAnimQueue.pop_front(); + + bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0); + mAnimation->play(mAnimQueue.front().mGroup, Priority_Default, + MWRender::Animation::BlendMask_All, false, + 1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount, loopfallback); + } + } +} + void CharacterController::update(float duration) { MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -1537,21 +1554,7 @@ void CharacterController::update(float duration) mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, mAnimQueue.size() <= 1); if(!cls.isActor()) - { - if(mAnimQueue.size() > 1) - { - if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false) - { - mAnimation->disable(mAnimQueue.front().mGroup); - mAnimQueue.pop_front(); - - bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0); - mAnimation->play(mAnimQueue.front().mGroup, Priority_Default, - MWRender::Animation::BlendMask_All, false, - 1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount, loopfallback); - } - } - } + updateAnimQueue(); else if(!cls.getCreatureStats(mPtr).isDead()) { bool onground = world->isOnGround(mPtr); @@ -1819,19 +1822,8 @@ void CharacterController::update(float duration) { idlestate = (inwater ? CharState_IdleSwim : (sneak && !inJump ? CharState_IdleSneak : CharState_Idle)); } - else if(mAnimQueue.size() > 1) - { - if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false) - { - mAnimation->disable(mAnimQueue.front().mGroup); - mAnimQueue.pop_front(); - - bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0); - mAnimation->play(mAnimQueue.front().mGroup, Priority_Default, - MWRender::Animation::BlendMask_All, false, - 1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount, loopfallback); - } - } + else + updateAnimQueue(); if (!mSkipAnim) { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index d5dc5fe28..4661d6983 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -212,6 +212,8 @@ class CharacterController : public MWRender::Animation::TextKeyListener bool updateCreatureState(); void updateIdleStormState(bool inwater); + void updateAnimQueue(); + void updateHeadTracking(float duration); void updateMagicEffects(); From b0dc625b185b8d21a4808806790d36eafcec5eb2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Aug 2016 23:03:26 +0200 Subject: [PATCH 16/19] Run setLoopingEnabled after the anim queue is updated --- apps/openmw/mwmechanics/character.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 873d3ce7e..319e3074f 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1539,6 +1539,9 @@ void CharacterController::updateAnimQueue() 1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount, loopfallback); } } + + if(!mAnimQueue.empty()) + mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, mAnimQueue.size() <= 1); } void CharacterController::update(float duration) @@ -1550,9 +1553,6 @@ void CharacterController::update(float duration) updateMagicEffects(); - if(!mAnimQueue.empty()) - mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, mAnimQueue.size() <= 1); - if(!cls.isActor()) updateAnimQueue(); else if(!cls.getCreatureStats(mPtr).isDead()) From 0fd810707ef21f8c7a904dd2dd6e1030491b9185 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Aug 2016 23:04:05 +0200 Subject: [PATCH 17/19] Remove unused stopLooping() --- apps/openmw/mwrender/animation.cpp | 10 ---------- apps/openmw/mwrender/animation.hpp | 4 ---- 2 files changed, 14 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 29407bc2d..32fa5e600 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -873,16 +873,6 @@ namespace MWRender addControllers(); } - void Animation::stopLooping(const std::string& groupname) - { - AnimStateMap::iterator stateiter = mStates.find(groupname); - if(stateiter != mStates.end()) - { - stateiter->second.mLoopCount = 0; - return; - } - } - void Animation::adjustSpeedMult(const std::string &groupname, float speedmult) { AnimStateMap::iterator state(mStates.find(groupname)); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 749890ae5..d0191d172 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -395,10 +395,6 @@ public: float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops, bool loopfallback=false); - /** If the given animation group is currently playing, set its remaining loop count to '0'. - */ - void stopLooping(const std::string& groupName); - /** Adjust the speed multiplier of an already playing animation. */ void adjustSpeedMult (const std::string& groupname, float speedmult); From 9b0e5d6b5900a3cdef293951f5ea2e24a0ac4924 Mon Sep 17 00:00:00 2001 From: Allofich Date: Tue, 23 Aug 2016 19:50:56 +0900 Subject: [PATCH 18/19] Loop mid-animation idles when loading a save game --- apps/openmw/mwmechanics/character.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 319e3074f..637c24300 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1989,9 +1989,10 @@ void CharacterController::unpersistAnimationState() mCurrentIdle.clear(); mIdleState = CharState_SpecialIdle; + bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0); mAnimation->play(anim.mGroup, Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f, - "start", "stop", complete, anim.mLoopCount); + "start", "stop", complete, anim.mLoopCount, loopfallback); } } From 7db31ab58a8e97735bb9c16a7e79f6b99fbd1b61 Mon Sep 17 00:00:00 2001 From: Allofich Date: Tue, 23 Aug 2016 23:13:37 +0900 Subject: [PATCH 19/19] Correct telekinesis glow length --- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 6 +++--- apps/openmw/mwrender/animation.hpp | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 99d4a6011..7494d2b43 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -124,7 +124,7 @@ namespace MWClass int index = ESM::MagicEffect::effectStringToId("sEffectTelekinesis"); const ESM::MagicEffect *effect = store.get().find(index); - animation->addSpellCastGlow(effect); // TODO: Telekinesis glow should only be as long as the door animation + animation->addSpellCastGlow(effect, 1); // 1 second glow to match the time taken for a door opening or closing } // make key id lowercase diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 32fa5e600..386a4a53b 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1187,7 +1187,7 @@ namespace MWRender int mLowestUnusedTexUnit; }; - void Animation::addSpellCastGlow(const ESM::MagicEffect *effect) + void Animation::addSpellCastGlow(const ESM::MagicEffect *effect, float glowDuration) { osg::Vec4f glowColor(1,1,1,1); glowColor.x() = effect->mData.mRed / 255.f; @@ -1202,10 +1202,10 @@ namespace MWRender if (mGlowUpdater && mGlowUpdater->isPermanentGlowUpdater()) { mGlowUpdater->setColor(glowColor); - mGlowUpdater->setDuration(1.5); // Glow length measured from original engine as about 1.5 seconds + mGlowUpdater->setDuration(glowDuration); } else - addGlow(mObjectRoot, glowColor, 1.5); + addGlow(mObjectRoot, glowColor, glowDuration); } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index d0191d172..089d0d85b 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -360,7 +360,9 @@ public: void removeEffect (int effectId); void getLoopingEffects (std::vector& out) const; - void addSpellCastGlow(const ESM::MagicEffect *effect); + // Add a spell casting glow to an object. From measuring video taken from the original engine, + // the glow seems to be about 1.5 seconds except for telekinesis, which is 1 second. + void addSpellCastGlow(const ESM::MagicEffect *effect, float glowDuration = 1.5); virtual void updatePtr(const MWWorld::Ptr &ptr);