diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4eeea6f1f6..6a39178ce0 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -36,13 +36,13 @@ namespace float getZAngleToDir(const osg::Vec3f& dir) { - return osg::RadiansToDegrees(std::atan2(dir.x(), dir.y())); + return std::atan2(dir.x(), dir.y()); } float getXAngleToDir(const osg::Vec3f& dir, float dirLen = 0.0f) { float len = (dirLen > 0.0f)? dirLen : dir.length(); - return osg::RadiansToDegrees(-std::asin(dir.z() / len)); + return -std::asin(dir.z() / len); } @@ -221,12 +221,12 @@ namespace MWMechanics if(movement.mRotation[2] != 0) { - if(zTurn(actor, osg::DegreesToRadians(movement.mRotation[2]))) movement.mRotation[2] = 0; + if(zTurn(actor, movement.mRotation[2])) movement.mRotation[2] = 0; } if(movement.mRotation[0] != 0) { - if(smoothTurn(actor, osg::DegreesToRadians(movement.mRotation[0]), 0)) movement.mRotation[0] = 0; + if(smoothTurn(actor, movement.mRotation[0], 0)) movement.mRotation[0] = 0; } bool& attack = storage.mAttack; diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 22c9075887..0e68d9d79b 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -104,7 +104,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po actor.getClass().getMovementSettings(actor).mPosition[0] = 1; actor.getClass().getMovementSettings(actor).mPosition[1] = 1; // change the angle a bit, too - zTurn(actor, osg::DegreesToRadians(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]))); + zTurn(actor, mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])); } } else { //Not stuck, so reset things @@ -117,7 +117,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward the rest of the time } - zTurn(actor, osg::DegreesToRadians(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + zTurn(actor, mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])); return false; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 1375b5807a..5c9949a5a3 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -51,10 +51,10 @@ namespace MWMechanics /// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive. struct AiWanderStorage : AiTemporaryBase { - // the z rotation angle (degrees) we want to reach - // used every frame when mRotate is true + // the z rotation angle to reach + // when mTurnActorGivingGreetingToFacePlayer is true float mTargetAngleRadians; - bool mRotate; + bool mTurnActorGivingGreetingToFacePlayer; float mReaction; // update some actions infrequently @@ -64,27 +64,21 @@ namespace MWMechanics const MWWorld::CellStore* mCell; // for detecting cell change // AiWander states - bool mChooseAction; - bool mIdleNow; - bool mMoveNow; - bool mWalking; + AiWander::WanderState mState; - unsigned short mPlayedIdle; + unsigned short mIdleAnimation; PathFinder mPathFinder; AiWanderStorage(): mTargetAngleRadians(0), - mRotate(false), + mTurnActorGivingGreetingToFacePlayer(false), mReaction(0), mSaidGreeting(AiWander::Greet_None), mGreetingTimer(0), mCell(NULL), - mChooseAction(true), - mIdleNow(false), - mMoveNow(false), - mWalking(false), - mPlayedIdle(0) + mState(AiWander::Wander_ChooseAction), + mIdleAnimation(0) {}; }; @@ -200,88 +194,77 @@ namespace MWMechanics ESM::Position pos = actor.getRefData().getPosition(); - bool& idleNow = storage.mIdleNow; - bool& moveNow = storage.mMoveNow; - bool& walking = storage.mWalking; + WanderState& wanderState = storage.mState; // Check if an idle actor is too close to a door - if so start walking mDoorCheckDuration += duration; if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL) { mDoorCheckDuration = 0; // restart timer if(mDistance && // actor is not intended to be stationary - idleNow && // but is in idle - !walking && // FIXME: some actors are idle while walking + (wanderState == Wander_IdleNow) && // but is in idle proximityToDoor(actor, MIN_DIST_TO_DOOR_SQUARED*1.6f*1.6f)) // NOTE: checks interior cells only { - idleNow = false; - moveNow = true; + wanderState = Wander_MoveNow; mTrimCurrentNode = false; // just in case } } // Are we there yet? - bool& chooseAction = storage.mChooseAction; - if(walking && + if ((wanderState == Wander_Walking) && storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE)) { stopWalking(actor, storage); - chooseAction = true; + wanderState = Wander_ChooseAction; mHasReturnPosition = false; } - if(walking) // have not yet reached the destination + if (wanderState == Wander_Walking) // have not yet reached the destination { // turn towards the next point in mPath - zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + zTurn(actor, storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])); actor.getClass().getMovementSettings(actor).mPosition[1] = 1; evadeObstacles(actor, storage, duration); } - - float& targetAngleRadians = storage.mTargetAngleRadians; - bool& rotate = storage.mRotate; + bool& rotate = storage.mTurnActorGivingGreetingToFacePlayer; if (rotate) { // Reduce the turning animation glitch by using a *HUGE* value of // epsilon... TODO: a proper fix might be in either the physics or the // animation subsystem - if (zTurn(actor, targetAngleRadians, osg::DegreesToRadians(5.f))) + if (zTurn(actor, storage.mTargetAngleRadians, osg::DegreesToRadians(5.f))) rotate = false; } // Check if idle animation finished - short unsigned& playedIdle = storage.mPlayedIdle; + short unsigned& idleAnimation = storage.mIdleAnimation; GreetingState& greetingState = storage.mSaidGreeting; - if(idleNow && !checkIdle(actor, playedIdle) && (greetingState == Greet_Done || greetingState == Greet_None)) + if ((wanderState == Wander_IdleNow) && + !checkIdle(actor, idleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None)) { - playedIdle = 0; - idleNow = false; - chooseAction = true; + wanderState = Wander_ChooseAction; } MWBase::World *world = MWBase::Environment::get().getWorld(); - if(chooseAction) + if (wanderState == Wander_ChooseAction) { - playedIdle = 0; - getRandomIdle(playedIdle); // NOTE: sets mPlayedIdle with a random selection + idleAnimation = getRandomIdle(); - if(!playedIdle && mDistance) + if(!idleAnimation && mDistance) { - chooseAction = false; - moveNow = true; + wanderState = Wander_MoveNow; } else { // Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander: MWWorld::TimeStamp currentTime = world->getTimeStamp(); mStartTime = currentTime; - playIdle(actor, playedIdle); - chooseAction = false; - idleNow = true; + playIdle(actor, idleAnimation); + wanderState = Wander_IdleNow; } } @@ -334,9 +317,6 @@ namespace MWMechanics mHasReturnPosition = false; if (mDistance == 0 && mHasReturnPosition && (pos.asVec3() - mReturnPosition).length2() > 20*20) { - chooseAction = false; - idleNow = false; - if (!storage.mPathFinder.isPathConstructed()) { ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mReturnPosition)); @@ -349,19 +329,18 @@ namespace MWMechanics if(storage.mPathFinder.isPathConstructed()) { - moveNow = false; - walking = true; + wanderState = Wander_Walking; } } } // Allow interrupting a walking actor to trigger a greeting - if(idleNow || walking) + if ((wanderState == Wander_IdleNow) || (wanderState == Wander_Walking)) { playGreetingIfPlayerGetsTooClose(actor, storage); } - if(moveNow && mDistance) + if ((wanderState == Wander_MoveNow) && mDistance) { // Construct a new path if there isn't one if(!storage.mPathFinder.isPathConstructed()) @@ -388,8 +367,7 @@ namespace MWMechanics trimAllowedNodes(mAllowedNodes, storage.mPathFinder); mObstacleCheck.clear(); storage.mPathFinder.clearPath(); - storage.mWalking = false; - storage.mMoveNow = true; + storage.mState = Wander_MoveNow; } else // probably walking into another NPC { @@ -399,7 +377,7 @@ namespace MWMechanics actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f; // change the angle a bit, too const ESM::Position& pos = actor.getRefData().getPosition(); - zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]))); + zTurn(actor, storage.mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])); } mStuckCount++; // TODO: maybe no longer needed } @@ -411,7 +389,7 @@ namespace MWMechanics mObstacleCheck.clear(); stopWalking(actor, storage); - storage.mChooseAction = true; + storage.mState = Wander_ChooseAction; mStuckCount = 0; } //#endif @@ -483,22 +461,14 @@ namespace MWMechanics { greetingTimer++; - if (storage.mWalking) + if (storage.mState == Wander_Walking) { stopWalking(actor, storage); mObstacleCheck.clear(); - storage.mIdleNow = true; - getRandomIdle(storage.mPlayedIdle); + storage.mState = Wander_IdleNow; } - if (!storage.mRotate) - { - osg::Vec3f dir = playerPos - actorPos; - - float faceAngleRadians = std::atan2(dir.x(), dir.y()); - storage.mTargetAngleRadians = faceAngleRadians; - storage.mRotate = true; - } + turnActorToFacePlayer(actorPos, playerPos, storage); if (greetingTimer >= GREETING_SHOULD_END) { @@ -515,6 +485,15 @@ namespace MWMechanics } } + void AiWander::turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage) + { + osg::Vec3f dir = playerPosition - actorPosition; + + float faceAngleRadians = std::atan2(dir.x(), dir.y()); + storage.mTargetAngleRadians = faceAngleRadians; + storage.mTurnActorGivingGreetingToFacePlayer = true; + } + void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos) { unsigned int randNode = Misc::Rng::rollDice(mAllowedNodes.size()); @@ -539,8 +518,7 @@ namespace MWMechanics mAllowedNodes.push_back(mCurrentNode); mCurrentNode = temp; - storage.mMoveNow = false; - storage.mWalking = true; + storage.mState = Wander_Walking; } // Choose a different node and delete this one from possible nodes because it is uncreachable: else @@ -591,8 +569,6 @@ namespace MWMechanics { storage.mPathFinder.clearPath(); actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - storage.mMoveNow = false; - storage.mWalking = false; } void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) @@ -626,9 +602,10 @@ namespace MWMechanics } } - void AiWander::getRandomIdle(short unsigned& playedIdle) + short unsigned AiWander::getRandomIdle() { unsigned short idleRoll = 0; + short unsigned selectedAnimation = 0; for(unsigned int counter = 0; counter < mIdle.size(); counter++) { @@ -639,10 +616,11 @@ namespace MWMechanics unsigned short randSelect = (int)(Misc::Rng::rollProbability() * int(100 / fIdleChanceMultiplier)); if(randSelect < idleChance && randSelect > idleRoll) { - playedIdle = counter+2; + selectedAnimation = counter + GroupIndex_MinIdle; idleRoll = randSelect; } } + return selectedAnimation; } void AiWander::fastForward(const MWWorld::Ptr& actor, AiState &state) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 82baeedf36..d1dab3f50e 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -63,6 +63,13 @@ namespace MWMechanics Greet_InProgress, Greet_Done }; + + enum WanderState { + Wander_ChooseAction, + Wander_IdleNow, + Wander_MoveNow, + Wander_Walking + }; private: // NOTE: mDistance and mDuration must be set already void init(); @@ -70,11 +77,12 @@ namespace MWMechanics void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); - void getRandomIdle(unsigned short& playedIdle); + short unsigned getRandomIdle(); void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos); void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage); void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration); void playIdleDialogueRandomly(const MWWorld::Ptr& actor); + void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage); int mDistance; // how far the actor can wander from the spawn point int mDuration; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 3c1a40fe0e..79cee9f326 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -264,7 +264,7 @@ namespace MWMechanics float directionX = nextPoint.mX - x; float directionY = nextPoint.mY - y; - return osg::RadiansToDegrees(std::atan2(directionX, directionY)); + return std::atan2(directionX, directionY); } bool PathFinder::checkPathCompleted(float x, float y, float tolerance) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index a4886a8400..4000f5d800 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -40,7 +40,7 @@ namespace MWMechanics bool checkPathCompleted(float x, float y, float tolerance = PathTolerance); ///< \Returns true if we are within \a tolerance units of the last path point. - /// In degrees + /// In radians float getZAngleToNext(float x, float y) const; bool isPathConstructed() const