diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 19599af339..40b9a2146e 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -81,6 +81,48 @@ namespace MWMechanics static const float DOOR_CHECK_INTERVAL = 1.5f; // same as AiWander // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp + + /// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive. + struct AiCombatStorage : AiTemporaryBase + { + float mTimerAttack; + float mTimerReact; + float mTimerCombatMove; + bool mReadyToAttack; + bool mAttack; + bool mFollowTarget; + bool mCombatMove; + Ogre::Vector3 mLastTargetPos; + const MWWorld::CellStore* mCell; + boost::shared_ptr mCurrentAction; + float mActionCooldown; + float mStrength; + float mMinMaxAttackDuration[3][2]; + bool mMinMaxAttackDurationInitialised; + bool mForceNoShortcut; + ESM::Position mShortcutFailPos; + Ogre::Vector3 mLastActorPos; + MWMechanics::Movement mMovement; + + AiCombatStorage(): + mTimerAttack(0), + mTimerReact(0), + mTimerCombatMove(0), + mAttack(false), + mFollowTarget(false), + mCombatMove(false), + mReadyToAttack(false), + mForceNoShortcut(false), + mCell(NULL), + mCurrentAction(), + mActionCooldown(0), + mStrength(), + mMinMaxAttackDurationInitialised(false), + mLastTargetPos(0,0,0), + mLastActorPos(0,0,0), + mMovement(){} + }; + AiCombat::AiCombat(const MWWorld::Ptr& actor) : mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()) {} @@ -157,8 +199,8 @@ namespace MWMechanics Ogre::Vector3& lastTargetPos = storage.mLastTargetPos; const MWWorld::CellStore*& currentCell = storage.mCell; boost::shared_ptr& currentAction = storage.mCurrentAction; - float actionCooldown = storage.mActionCooldown; - float strength = storage.mStrength; + float& actionCooldown = storage.mActionCooldown; + float& strength = storage.mStrength; float (&minMaxAttackDuration)[3][2] = storage.mMinMaxAttackDuration; bool& minMaxAttackDurationInitialised = storage.mMinMaxAttackDurationInitialised; bool& forceNoShortcut = storage.mForceNoShortcut; diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 3b1f1e4e46..307df3872c 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -61,56 +61,7 @@ namespace MWMechanics void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); }; - struct AiCombatStorage : AiTemporaryBase - { - // controls duration of the actual strike - float mTimerAttack; - float mTimerReact; - // controls duration of the sideway & forward moves - // when mCombatMove is true - float mTimerCombatMove; - - // AiCombat states - bool mReadyToAttack, mAttack; - bool mFollowTarget; - bool mCombatMove; - - Ogre::Vector3 mLastTargetPos; - - const MWWorld::CellStore* mCell; - - boost::shared_ptr mCurrentAction; - float mActionCooldown; - - float mStrength; // this is actually make sense only in ranged combat - float mMinMaxAttackDuration[3][2]; // slash, thrust, chop has different durations - bool mMinMaxAttackDurationInitialised; - - bool mForceNoShortcut; - ESM::Position mShortcutFailPos; - - Ogre::Vector3 mLastActorPos; - MWMechanics::Movement mMovement; - - AiCombatStorage() - { - mActionCooldown = 0; - mTimerAttack = 0; - mTimerReact = 0; - mTimerCombatMove = 0; - mFollowTarget = false; - mReadyToAttack = false; - mAttack = false; - mCombatMove = false; - mForceNoShortcut = false; - mStrength = 0; - mCell = NULL; - mLastTargetPos = Ogre::Vector3(0,0,0); - mLastActorPos = Ogre::Vector3(0,0,0); - mMinMaxAttackDurationInitialised = false; - } - - }; + } #endif diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index b3b7888993..7826df7082 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -28,6 +28,55 @@ namespace MWMechanics static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player static const int GREETING_SHOULD_END = 10; + /// \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 + float mTargetAngle; + bool mRotate; + float mReaction; // update some actions infrequently + + + AiWander::GreetingState mSaidGreeting; + int mGreetingTimer; + + // Cached current cell location + int mCellX; + int mCellY; + // Cell location multiplied by ESM::Land::REAL_SIZE + float mXCell; + float mYCell; + + const MWWorld::CellStore* mCell; // for detecting cell change + + // AiWander states + bool mChooseAction; + bool mIdleNow; + bool mMoveNow; + bool mWalking; + + unsigned short mPlayedIdle; + + AiWanderStorage(): + mTargetAngle(0), + mRotate(false), + mReaction(0), + mSaidGreeting(AiWander::Greet_None), + mGreetingTimer(0), + mCellX(std::numeric_limits::max()), + mCellY(std::numeric_limits::max()), + mXCell(0), + mYCell(0), + mCell(NULL), + mChooseAction(true), + mIdleNow(false), + mMoveNow(false), + mWalking(false), + mPlayedIdle(0) + {}; + }; + AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) { @@ -55,13 +104,9 @@ namespace MWMechanics mTimeOfDay = 0; mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); - mPlayedIdle = 0; mStoredAvailableNodes = false; - mChooseAction = true; - mIdleNow = false; - mMoveNow = false; - mWalking = false; + } AiPackage * MWMechanics::AiWander::clone() const @@ -131,9 +176,14 @@ namespace MWMechanics int& greetingTimer = storage.mGreetingTimer; int& cachedCellX = storage.mCellX; int& cachedCellY = storage.mCellY; - float& cachedCellXf = storage.mXCell; - float& cachedCellYf = storage.mYCell; + float& cachedCellXposition = storage.mXCell; + float& cachedCellYposition = storage.mYCell; const MWWorld::CellStore*& currentCell = storage.mCell; + bool& chooseAction = storage.mChooseAction; + bool& idleNow = storage.mIdleNow; + bool& moveNow = storage.mMoveNow; + bool& walking = storage.mWalking; + short unsigned& playedIdle = storage.mPlayedIdle; MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); @@ -159,28 +209,28 @@ namespace MWMechanics { mDoorCheckDuration = 0; // restart timer if(mDistance && // actor is not intended to be stationary - mIdleNow && // but is in idle - !mWalking && // FIXME: some actors are idle while walking + idleNow && // but is in idle + !walking && // FIXME: some actors are idle while walking proximityToDoor(actor, MIN_DIST_TO_DOOR_SQUARED*1.6*1.6)) // NOTE: checks interior cells only { - mIdleNow = false; - mMoveNow = true; + idleNow = false; + moveNow = true; mTrimCurrentNode = false; // just in case } } // Are we there yet? - if(mWalking && + if(walking && mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) { stopWalking(actor); - mMoveNow = false; - mWalking = false; - mChooseAction = true; + moveNow = false; + walking = false; + chooseAction = true; mHasReturnPosition = false; } - if(mWalking) // have not yet reached the destination + if(walking) // have not yet reached the destination { // turn towards the next point in mPath zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); @@ -197,8 +247,8 @@ namespace MWMechanics trimAllowedNodes(mAllowedNodes, mPathFinder); mObstacleCheck.clear(); mPathFinder.clearPath(); - mWalking = false; - mMoveNow = true; + walking = false; + moveNow = true; } else // probably walking into another NPC { @@ -219,9 +269,9 @@ namespace MWMechanics mObstacleCheck.clear(); stopWalking(actor); - mMoveNow = false; - mWalking = false; - mChooseAction = true; + moveNow = false; + walking = false; + chooseAction = true; } //#endif } @@ -295,12 +345,12 @@ namespace MWMechanics // destinations within the allowed set of pathgrid points (nodes). if(mDistance) { - cachedCellXf = 0; - cachedCellYf = 0; + cachedCellXposition = 0; + cachedCellYposition = 0; if(cell->isExterior()) { - cachedCellXf = cachedCellX * ESM::Land::REAL_SIZE; - cachedCellYf = cachedCellYf * ESM::Land::REAL_SIZE; + cachedCellXposition = cachedCellX * ESM::Land::REAL_SIZE; + cachedCellYposition = cachedCellY * ESM::Land::REAL_SIZE; } // FIXME: There might be a bug here. The allowed node points are @@ -310,8 +360,8 @@ namespace MWMechanics // // convert npcPos to local (i.e. cell) co-ordinates Ogre::Vector3 npcPos(pos.pos); - npcPos[0] = npcPos[0] - cachedCellXf; - npcPos[1] = npcPos[1] - cachedCellYf; + npcPos[0] = npcPos[0] - cachedCellXposition; + npcPos[1] = npcPos[1] - cachedCellYposition; // mAllowedNodes for this actor with pathgrid point indexes based on mDistance // NOTE: mPoints and mAllowedNodes are in local co-ordinates @@ -356,8 +406,8 @@ namespace MWMechanics mHasReturnPosition = false; if (mDistance == 0 && mHasReturnPosition && Ogre::Vector3(pos.pos).squaredDistance(mReturnPosition) > 20*20) { - mChooseAction = false; - mIdleNow = false; + chooseAction = false; + idleNow = false; if (!mPathFinder.isPathConstructed()) { @@ -379,30 +429,30 @@ namespace MWMechanics if(mPathFinder.isPathConstructed()) { - mMoveNow = false; - mWalking = true; + moveNow = false; + walking = true; } } } - if(mChooseAction) + if(chooseAction) { - mPlayedIdle = 0; - getRandomIdle(); // NOTE: sets mPlayedIdle with a random selection + playedIdle = 0; + getRandomIdle(playedIdle); // NOTE: sets mPlayedIdle with a random selection - if(!mPlayedIdle && mDistance) + if(!playedIdle && mDistance) { - mChooseAction = false; - mMoveNow = true; + chooseAction = false; + moveNow = true; } else { // Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander: MWWorld::TimeStamp currentTime = world->getTimeStamp(); mStartTime = currentTime; - playIdle(actor, mPlayedIdle); - mChooseAction = false; - mIdleNow = true; + playIdle(actor, playedIdle); + chooseAction = false; + idleNow = true; // Play idle voiced dialogue entries randomly int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); @@ -426,7 +476,7 @@ namespace MWMechanics } // Allow interrupting a walking actor to trigger a greeting - if(mIdleNow || mWalking) + if(idleNow || walking) { // Play a random voice greeting if the player gets too close int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); @@ -460,23 +510,21 @@ namespace MWMechanics { greetingTimer++; - if(mWalking) + if(walking) { stopWalking(actor); - mMoveNow = false; - mWalking = false; + moveNow = false; + walking = false; mObstacleCheck.clear(); - mIdleNow = true; - getRandomIdle(); + idleNow = true; + getRandomIdle(playedIdle); } if(!rotate) { Ogre::Vector3 dir = playerPos - actorPos; - float length = dir.length(); - float faceAngle = Ogre::Radian(Ogre::Math::ACos(dir.y / length) * - ((Ogre::Math::ASin(dir.x / length).valueRadians()>0)?1.0:-1.0)).valueDegrees(); + float faceAngle = Ogre::Math::ATan2(dir.x,dir.y).valueDegrees(); float actorAngle = actor.getRefData().getBaseNode()->getOrientation().getRoll().valueDegrees(); // an attempt at reducing the turning animation glitch if(abs(abs(faceAngle) - abs(actorAngle)) >= 5) // TODO: is there a better way? @@ -503,15 +551,15 @@ namespace MWMechanics } // Check if idle animation finished - if(!checkIdle(actor, mPlayedIdle) && (playerDistSqr > helloDistance*helloDistance || greetingState == MWMechanics::AiWander::Greet_Done)) + if(!checkIdle(actor, playedIdle) && (playerDistSqr > helloDistance*helloDistance || greetingState == MWMechanics::AiWander::Greet_Done)) { - mPlayedIdle = 0; - mIdleNow = false; - mChooseAction = true; + playedIdle = 0; + idleNow = false; + chooseAction = true; } } - if(mMoveNow && mDistance) + if(moveNow && mDistance) { // Construct a new path if there isn't one if(!mPathFinder.isPathConstructed()) @@ -525,8 +573,8 @@ namespace MWMechanics // convert dest to use world co-ordinates ESM::Pathgrid::Point dest; - dest.mX = destNodePos[0] + cachedCellXf; - dest.mY = destNodePos[1] + cachedCellYf; + dest.mX = destNodePos[0] + cachedCellXposition; + dest.mY = destNodePos[1] + cachedCellYposition; dest.mZ = destNodePos[2]; // actor position is already in world co-ordinates @@ -557,8 +605,8 @@ namespace MWMechanics mAllowedNodes.push_back(mCurrentNode); mCurrentNode = temp; - mMoveNow = false; - mWalking = true; + moveNow = false; + walking = true; } // Choose a different node and delete this one from possible nodes because it is uncreachable: else @@ -657,7 +705,7 @@ namespace MWMechanics } } - void AiWander::getRandomIdle() + void AiWander::getRandomIdle(short unsigned& playedIdle) { unsigned short idleRoll = 0; @@ -670,7 +718,7 @@ namespace MWMechanics unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / fIdleChanceMultiplier)); if(randSelect < idleChance && randSelect > idleRoll) { - mPlayedIdle = counter+2; + playedIdle = counter+2; idleRoll = randSelect; } } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 400897ed1b..0600909bae 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -68,7 +68,7 @@ namespace MWMechanics void stopWalking(const MWWorld::Ptr& actor); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); - void getRandomIdle(); + void getRandomIdle(unsigned short& playedIdle); int mDistance; // how far the actor can wander from the spawn point int mDuration; @@ -88,13 +88,9 @@ namespace MWMechanics // if false triggers calculating allowed nodes based on mDistance bool mStoredAvailableNodes; - // AiWander states - bool mChooseAction; - bool mIdleNow; - bool mMoveNow; - bool mWalking; - unsigned short mPlayedIdle; + + MWWorld::TimeStamp mStartTime; @@ -105,49 +101,16 @@ namespace MWMechanics void trimAllowedNodes(std::vector& nodes, const PathFinder& pathfinder); - PathFinder mPathFinder; +// PathFinder mPathFinder; - ObstacleCheck mObstacleCheck; +// ObstacleCheck mObstacleCheck; float mDoorCheckDuration; int mStuckCount; }; - /// \brief Temporary values used by AiWander - struct AiWanderStorage : AiTemporaryBase - { - // the z rotation angle (degrees) we want to reach - // used every frame when mRotate is true - float mTargetAngle; - bool mRotate; - float mReaction; // update some actions infrequently - - - AiWander::GreetingState mSaidGreeting; - int mGreetingTimer; - - // Cached current cell location - int mCellX; - int mCellY; - // Cell location multiplied by ESM::Land::REAL_SIZE - float mXCell; - float mYCell; - - const MWWorld::CellStore* mCell; // for detecting cell change - - AiWanderStorage():mTargetAngle(0),mRotate(false),mReaction(0) - { - mSaidGreeting = AiWander::Greet_None; - mGreetingTimer = 0; - - mCellX = std::numeric_limits::max(); - mCellY = std::numeric_limits::max(); - mXCell = 0; - mYCell = 0; - mCell = NULL; - }; - }; + } #endif