From 4f08084e7921603e2b590a645b1dbaa8d0777d87 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 30 Apr 2017 22:29:59 +0900 Subject: [PATCH 1/5] Make stationary actors return to position on load Also makes wandering actors resume their previous destination when an interrupting combat or pursuit ends. (Fixes #3656) --- apps/openmw/mwmechanics/aisequence.cpp | 15 ----- apps/openmw/mwmechanics/aiwander.cpp | 79 ++++++++++++++------------ apps/openmw/mwmechanics/aiwander.hpp | 16 ++---- 3 files changed, 49 insertions(+), 61 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 82a2e2c3d..2f33d6e4e 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -295,21 +295,6 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor) if (actor == getPlayer()) throw std::runtime_error("Can't add AI packages to player"); - if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPursue) - { - // Notify AiWander of our current position so we can return to it after combat finished - for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) - { - if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat - && (*iter)->getTarget() == (&package)->getTarget()) - { - return; // already in combat with this actor - } - else if ((*iter)->getTypeId() == AiPackage::TypeIdWander) - static_cast(*iter)->setReturnPosition(actor.getRefData().getPosition().asVec3()); - } - } - // Stop combat when a non-combat AI package is added if (isActualAiPackage(package.getTypeId())) stopCombat(); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index a992bc8d4..ad6db4d01 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -113,7 +113,7 @@ namespace MWMechanics AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), - mRepeat(repeat), mStoredInitialActorPosition(false) + mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)) { mIdle.resize(8, 0); init(); @@ -123,9 +123,6 @@ namespace MWMechanics { // NOTE: mDistance and mDuration must be set already - mHasReturnPosition = false; - mReturnPosition = osg::Vec3f(0,0,0); - if(mDistance < 0) mDistance = 0; if(mDuration < 0) @@ -211,6 +208,19 @@ namespace MWMechanics cStats.setMovementFlag(CreatureStats::Flag_Run, false); ESM::Position pos = actor.getRefData().getPosition(); + + // If there is already a destination due to the package having been interrupted by a combat or pursue package, + // rebuild a path to it + if (!mPathFinder.isPathConstructed() && mHasDestination) + { + ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mDestination)); + ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); + + mPathFinder.buildSyncedPath(start, dest, actor.getCell()); + + if (mPathFinder.isPathConstructed()) + storage.setState(Wander_Walking); + } doPerFrameActionsForState(actor, duration, storage, pos); @@ -253,9 +263,8 @@ namespace MWMechanics getAllowedNodes(actor, currentCell->getCell(), storage); } - // Actor becomes stationary - see above URL's for previous research - // If a creature or an NPC with a wander distance and no pathgrid is available, - // randomly idle or wander around near spawn point + // If the package has a wander distance but no pathgrid is available, + // randomly idle or wander near spawn point if(storage.mAllowedNodes.empty() && mDistance > 0 && !storage.mIsWanderingManually) { // Typically want to idle for a short time before the next wander if (Misc::Rng::rollDice(100) >= 96) { @@ -277,10 +286,8 @@ namespace MWMechanics mDistance = 0; // For stationary NPCs, move back to the starting location if another AiPackage moved us elsewhere - if (cellChange) - mHasReturnPosition = false; - if (mDistance == 0 && mHasReturnPosition - && (pos.asVec3() - mReturnPosition).length2() > (DESTINATION_TOLERANCE * DESTINATION_TOLERANCE)) + if (mDistance == 0 && !cellChange + && (pos.asVec3() - mInitialActorPosition).length2() > (DESTINATION_TOLERANCE * DESTINATION_TOLERANCE)) { returnToStartLocation(actor, storage, pos); } @@ -334,7 +341,8 @@ namespace MWMechanics { if (!mPathFinder.isPathConstructed()) { - ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mReturnPosition)); + mDestination = mInitialActorPosition; + ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mDestination)); // actor position is already in world coordinates ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); @@ -345,6 +353,7 @@ namespace MWMechanics if (mPathFinder.isPathConstructed()) { storage.setState(Wander_Walking); + mHasDestination = true; } } } @@ -357,7 +366,6 @@ namespace MWMechanics const osg::Vec3f currentPositionVec3f = osg::Vec3f(currentPosition.mX, currentPosition.mY, currentPosition.mZ); std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here - osg::Vec3f destination; ESM::Pathgrid::Point destinationPosition; bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); do { @@ -369,17 +377,18 @@ namespace MWMechanics const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection); const float destinationZ = mInitialActorPosition.z(); destinationPosition = ESM::Pathgrid::Point(destinationX, destinationY, destinationZ); - destination = osg::Vec3f(destinationX, destinationY, destinationZ); + mDestination = osg::Vec3f(destinationX, destinationY, destinationZ); // Check if land creature will walk onto water or if water creature will swim onto land - if ((!isWaterCreature && !destinationIsAtWater(actor, destination)) || - (isWaterCreature && !destinationThroughGround(currentPositionVec3f, destination))) { + if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) || + (isWaterCreature && !destinationThroughGround(currentPositionVec3f, mDestination))) { mPathFinder.buildSyncedPath(currentPosition, destinationPosition, actor.getCell()); mPathFinder.addPointToPath(destinationPosition); if (mPathFinder.isPathConstructed()) { storage.setState(Wander_Walking, true); + mHasDestination = true; } return; } @@ -466,7 +475,10 @@ namespace MWMechanics GreetingState& greetingState = storage.mSaidGreeting; if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None)) { - storage.setState(Wander_ChooseAction); + if (mPathFinder.isPathConstructed()) + storage.setState(Wander_Walking); + else + storage.setState(Wander_ChooseAction); } } @@ -478,7 +490,6 @@ namespace MWMechanics { stopWalking(actor, storage); storage.setState(Wander_ChooseAction); - mHasReturnPosition = false; } else { @@ -490,8 +501,8 @@ namespace MWMechanics void AiWander::onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage) { - short unsigned& idleAnimation = storage.mIdleAnimation; - idleAnimation = getRandomIdle(); + unsigned short idleAnimation = getRandomIdle(); + storage.mIdleAnimation = idleAnimation; if (!idleAnimation && mDistance) { @@ -525,7 +536,7 @@ namespace MWMechanics storage.mTrimCurrentNode = true; trimAllowedNodes(storage.mAllowedNodes, mPathFinder); mObstacleCheck.clear(); - mPathFinder.clearPath(); + stopWalking(actor, storage); storage.setState(Wander_MoveNow); } @@ -535,9 +546,7 @@ namespace MWMechanics // if stuck for sufficiently long, act like current location was the destination if (storage.mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset { - //std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl; mObstacleCheck.clear(); - stopWalking(actor, storage); storage.setState(Wander_ChooseAction); storage.mStuckCount = 0; @@ -612,7 +621,7 @@ namespace MWMechanics if (storage.mState == Wander_Walking) { - stopWalking(actor, storage); + stopWalking(actor, storage, false); mObstacleCheck.clear(); storage.setState(Wander_IdleNow); } @@ -657,6 +666,8 @@ namespace MWMechanics if (mPathFinder.isPathConstructed()) { + mDestination = osg::Vec3f(dest.mX, dest.mY, dest.mZ); + mHasDestination = true; // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): ESM::Pathgrid::Point temp = storage.mAllowedNodes[randNode]; storage.mAllowedNodes.erase(storage.mAllowedNodes.begin() + randNode); @@ -710,9 +721,13 @@ namespace MWMechanics return TypeIdWander; } - void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage) + void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage, bool clearPath) { - mPathFinder.clearPath(); + if (clearPath) + { + mPathFinder.clearPath(); + mHasDestination = false; + } actor.getClass().getMovementSettings(actor).mPosition[1] = 0; } @@ -743,15 +758,6 @@ namespace MWMechanics } } - void AiWander::setReturnPosition(const osg::Vec3f& position) - { - if (!mHasReturnPosition) - { - mHasReturnPosition = true; - mReturnPosition = position; - } - } - short unsigned AiWander::getRandomIdle() { unsigned short idleRoll = 0; @@ -945,6 +951,8 @@ namespace MWMechanics , mTimeOfDay(wander->mData.mTimeOfDay) , mRepeat(wander->mData.mShouldRepeat != 0) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) + , mHasDestination(false) + , mDestination(osg::Vec3f(0, 0, 0)) { if (mStoredInitialActorPosition) mInitialActorPosition = wander->mInitialActorPosition; @@ -953,7 +961,6 @@ namespace MWMechanics if (mRemainingDuration <= 0 || mRemainingDuration >= 24) mRemainingDuration = mDuration; - init(); } } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 13a4a1b4a..01d889e2f 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -44,10 +44,6 @@ namespace MWMechanics virtual int getTypeId() const; - /// Set the position to return to for a stationary (non-wandering) actor - /** In case another AI package moved the actor elsewhere **/ - void setReturnPosition (const osg::Vec3f& position); - virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); @@ -70,7 +66,7 @@ namespace MWMechanics private: // NOTE: mDistance and mDuration must be set already void init(); - void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage); + void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage, bool clearPath = true); /// Have the given actor play an idle animation /// @return Success or error @@ -87,7 +83,7 @@ namespace MWMechanics void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos); void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage); bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, - const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos, float duration); + const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos, float duration); bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage); void returnToStartLocation(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos); void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance); @@ -102,11 +98,11 @@ namespace MWMechanics std::vector mIdle; bool mRepeat; - bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position, - // if we had the actor in the AiWander constructor... - osg::Vec3f mReturnPosition; - osg::Vec3f mInitialActorPosition; bool mStoredInitialActorPosition; + osg::Vec3f mInitialActorPosition; + + bool mHasDestination; + osg::Vec3f mDestination; void getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell, AiWanderStorage& storage); From b277bd782ea5ab5b7e0ac7107b8187d9e98f6bb5 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Thu, 15 Jun 2017 21:59:13 -0500 Subject: [PATCH 2/5] Adjust restock calculations to ignore filled soul gems. Bug #3684 --- apps/openmw/mwworld/containerstore.cpp | 14 ++++++++++++-- apps/openmw/mwworld/containerstore.hpp | 6 +++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index a4154e125..eef10b905 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -156,6 +156,16 @@ int MWWorld::ContainerStore::count(const std::string &id) return total; } +int MWWorld::ContainerStore::restockCount(const std::string &id) +{ + int total=0; + for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter) + if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id)) + if (iter->getCellRef().getSoul().empty()) + total += iter->getRefData().getCount(); + return total; +} + MWWorld::ContainerStoreListener* MWWorld::ContainerStore::getContListener() const { return mListener; @@ -512,7 +522,7 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW for (std::map, int>::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end();) { int spawnedCount = it->second; //How many items should be in shop originally - int itemCount = count(it->first.first); //How many items are there in shop now + int itemCount = restockCount(it->first.first); //How many items are there in shop now //If something was not sold if(itemCount >= spawnedCount) { @@ -578,7 +588,7 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW else { //Restocking static item - just restock to the max count - int currentCount = count(itemOrList); + int currentCount = restockCount(itemOrList); if (currentCount < std::abs(it->mCount)) addInitialItem(itemOrList, owner, -(std::abs(it->mCount) - currentCount), true); } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 21e126a32..f27ff1db9 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -162,8 +162,12 @@ namespace MWWorld /// If a compatible stack is found, the item's count is added to that stack, then the original is deleted. /// @return If the item was stacked, return the stack, otherwise return the old (untouched) item. - /// @return How many items with refID \a id are in this container? int count (const std::string& id); + ///< @return How many items with refID \a id are in this container? + + int restockCount (const std::string& id); + ///< Item count with restock adjustments (such as ignoring filled soul gems). + /// @return How many items with refID \a id are in this container? ContainerStoreListener* getContListener() const; void setContListener(ContainerStoreListener* listener); From fad760cc1d667794288e103c86fc381c15eeeca0 Mon Sep 17 00:00:00 2001 From: tlmullis Date: Mon, 19 Jun 2017 05:49:04 -0700 Subject: [PATCH 3/5] msbuild was always defaulting to building the Debug configuration so it was impossible to build any other configurations with build.msvc.sh --- CI/build.msvc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/build.msvc.sh b/CI/build.msvc.sh index f8d5a2f24..eac969b0d 100644 --- a/CI/build.msvc.sh +++ b/CI/build.msvc.sh @@ -77,9 +77,9 @@ if [ $? -ne 0 ]; then fi if [ -z $APPVEYOR ]; then - msbuild OpenMW.sln //t:Build //m:8 + msbuild OpenMW.sln //t:Build //p:Configuration=${CONFIGURATION} //m:8 else - msbuild OpenMW.sln //t:Build //m:8 //logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + msbuild OpenMW.sln //t:Build //p:Configuration=${CONFIGURATION} //m:8 //logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" fi RET=$? From 41fb17f39bd7182a8ef55185c4f524a6df4e3669 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jun 2017 00:13:32 +0200 Subject: [PATCH 4/5] Revert "Correcting https://bugs.openmw.org/issues/3906" This reverts commit b0abed00e59aa8472daf0bb55bd6f5096e1d013b. --- apps/launcher/maindialog.cpp | 8 +------- apps/launcher/maindialog.hpp | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 46340dd9e..94e186db8 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -172,9 +172,6 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog() } } - if(!setupGameData()) - return FirstRunDialogResultFailure; - return setup() ? FirstRunDialogResultContinue : FirstRunDialogResultFailure; } @@ -347,10 +344,6 @@ bool Launcher::MainDialog::setupGameSettings() file.close(); } - return true; -} - -bool Launcher::MainDialog::setupGameData() { QStringList dataDirs; // Check if the paths actually contain data files @@ -386,6 +379,7 @@ bool Launcher::MainDialog::setupGameData() { } } } + return true; } diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 8d0d61b8f..96b5c0b97 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -72,7 +72,6 @@ namespace Launcher bool setupLauncherSettings(); bool setupGameSettings(); bool setupGraphicsSettings(); - bool setupGameData(); void setVersionLabel(); From 54bb1b13cb3c6559988ca95c16fd476ee1045eb5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jun 2017 00:34:48 +0200 Subject: [PATCH 5/5] Change NCO/NCC flags to NC* (Fixes #3915) --- components/nifbullet/bulletnifloader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index e17c96b79..3865b17a5 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -203,8 +203,7 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, // affecting the entire subtree of this node Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e; - // not sure what the difference between NCO and NCC is, or if there even is one - if (sd->string == "NCO" || sd->string == "NCC") + if (Misc::StringUtils::ciCompareLen(sd->string, "NC", 2) == 0) { // No collision. Use an internal flag setting to mark this. flags |= 0x800;