Moved all variables in temporary storage for AiCombat and some more for AiWander.

+Buxfix for previous AiWander changes
deque
terrorfisch 10 years ago
parent cae948df96
commit 730abf6841

@ -81,6 +81,48 @@ namespace MWMechanics
static const float DOOR_CHECK_INTERVAL = 1.5f; // same as AiWander static const float DOOR_CHECK_INTERVAL = 1.5f; // same as AiWander
// NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp // 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<Action> 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) : AiCombat::AiCombat(const MWWorld::Ptr& actor) :
mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()) mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId())
{} {}
@ -157,8 +199,8 @@ namespace MWMechanics
Ogre::Vector3& lastTargetPos = storage.mLastTargetPos; Ogre::Vector3& lastTargetPos = storage.mLastTargetPos;
const MWWorld::CellStore*& currentCell = storage.mCell; const MWWorld::CellStore*& currentCell = storage.mCell;
boost::shared_ptr<Action>& currentAction = storage.mCurrentAction; boost::shared_ptr<Action>& currentAction = storage.mCurrentAction;
float actionCooldown = storage.mActionCooldown; float& actionCooldown = storage.mActionCooldown;
float strength = storage.mStrength; float& strength = storage.mStrength;
float (&minMaxAttackDuration)[3][2] = storage.mMinMaxAttackDuration; float (&minMaxAttackDuration)[3][2] = storage.mMinMaxAttackDuration;
bool& minMaxAttackDurationInitialised = storage.mMinMaxAttackDurationInitialised; bool& minMaxAttackDurationInitialised = storage.mMinMaxAttackDurationInitialised;
bool& forceNoShortcut = storage.mForceNoShortcut; bool& forceNoShortcut = storage.mForceNoShortcut;

@ -61,56 +61,7 @@ namespace MWMechanics
void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); 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<Action> 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 #endif

@ -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_START = 4; //how many reaction intervals should pass before NPC can greet player
static const int GREETING_SHOULD_END = 10; 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<int>::max()),
mCellY(std::numeric_limits<int>::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<unsigned char>& idle, bool repeat): AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
{ {
@ -55,13 +104,9 @@ namespace MWMechanics
mTimeOfDay = 0; mTimeOfDay = 0;
mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mPlayedIdle = 0;
mStoredAvailableNodes = false; mStoredAvailableNodes = false;
mChooseAction = true;
mIdleNow = false;
mMoveNow = false;
mWalking = false;
} }
AiPackage * MWMechanics::AiWander::clone() const AiPackage * MWMechanics::AiWander::clone() const
@ -131,9 +176,14 @@ namespace MWMechanics
int& greetingTimer = storage.mGreetingTimer; int& greetingTimer = storage.mGreetingTimer;
int& cachedCellX = storage.mCellX; int& cachedCellX = storage.mCellX;
int& cachedCellY = storage.mCellY; int& cachedCellY = storage.mCellY;
float& cachedCellXf = storage.mXCell; float& cachedCellXposition = storage.mXCell;
float& cachedCellYf = storage.mYCell; float& cachedCellYposition = storage.mYCell;
const MWWorld::CellStore*& currentCell = storage.mCell; 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); MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor);
@ -159,28 +209,28 @@ namespace MWMechanics
{ {
mDoorCheckDuration = 0; // restart timer mDoorCheckDuration = 0; // restart timer
if(mDistance && // actor is not intended to be stationary if(mDistance && // actor is not intended to be stationary
mIdleNow && // but is in idle idleNow && // but is in idle
!mWalking && // FIXME: some actors are idle while walking !walking && // FIXME: some actors are idle while walking
proximityToDoor(actor, MIN_DIST_TO_DOOR_SQUARED*1.6*1.6)) // NOTE: checks interior cells only proximityToDoor(actor, MIN_DIST_TO_DOOR_SQUARED*1.6*1.6)) // NOTE: checks interior cells only
{ {
mIdleNow = false; idleNow = false;
mMoveNow = true; moveNow = true;
mTrimCurrentNode = false; // just in case mTrimCurrentNode = false; // just in case
} }
} }
// Are we there yet? // Are we there yet?
if(mWalking && if(walking &&
mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{ {
stopWalking(actor); stopWalking(actor);
mMoveNow = false; moveNow = false;
mWalking = false; walking = false;
mChooseAction = true; chooseAction = true;
mHasReturnPosition = false; 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 // turn towards the next point in mPath
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
@ -197,8 +247,8 @@ namespace MWMechanics
trimAllowedNodes(mAllowedNodes, mPathFinder); trimAllowedNodes(mAllowedNodes, mPathFinder);
mObstacleCheck.clear(); mObstacleCheck.clear();
mPathFinder.clearPath(); mPathFinder.clearPath();
mWalking = false; walking = false;
mMoveNow = true; moveNow = true;
} }
else // probably walking into another NPC else // probably walking into another NPC
{ {
@ -219,9 +269,9 @@ namespace MWMechanics
mObstacleCheck.clear(); mObstacleCheck.clear();
stopWalking(actor); stopWalking(actor);
mMoveNow = false; moveNow = false;
mWalking = false; walking = false;
mChooseAction = true; chooseAction = true;
} }
//#endif //#endif
} }
@ -295,12 +345,12 @@ namespace MWMechanics
// destinations within the allowed set of pathgrid points (nodes). // destinations within the allowed set of pathgrid points (nodes).
if(mDistance) if(mDistance)
{ {
cachedCellXf = 0; cachedCellXposition = 0;
cachedCellYf = 0; cachedCellYposition = 0;
if(cell->isExterior()) if(cell->isExterior())
{ {
cachedCellXf = cachedCellX * ESM::Land::REAL_SIZE; cachedCellXposition = cachedCellX * ESM::Land::REAL_SIZE;
cachedCellYf = cachedCellYf * ESM::Land::REAL_SIZE; cachedCellYposition = cachedCellY * ESM::Land::REAL_SIZE;
} }
// FIXME: There might be a bug here. The allowed node points are // 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 // convert npcPos to local (i.e. cell) co-ordinates
Ogre::Vector3 npcPos(pos.pos); Ogre::Vector3 npcPos(pos.pos);
npcPos[0] = npcPos[0] - cachedCellXf; npcPos[0] = npcPos[0] - cachedCellXposition;
npcPos[1] = npcPos[1] - cachedCellYf; npcPos[1] = npcPos[1] - cachedCellYposition;
// mAllowedNodes for this actor with pathgrid point indexes based on mDistance // mAllowedNodes for this actor with pathgrid point indexes based on mDistance
// NOTE: mPoints and mAllowedNodes are in local co-ordinates // NOTE: mPoints and mAllowedNodes are in local co-ordinates
@ -356,8 +406,8 @@ namespace MWMechanics
mHasReturnPosition = false; mHasReturnPosition = false;
if (mDistance == 0 && mHasReturnPosition && Ogre::Vector3(pos.pos).squaredDistance(mReturnPosition) > 20*20) if (mDistance == 0 && mHasReturnPosition && Ogre::Vector3(pos.pos).squaredDistance(mReturnPosition) > 20*20)
{ {
mChooseAction = false; chooseAction = false;
mIdleNow = false; idleNow = false;
if (!mPathFinder.isPathConstructed()) if (!mPathFinder.isPathConstructed())
{ {
@ -379,30 +429,30 @@ namespace MWMechanics
if(mPathFinder.isPathConstructed()) if(mPathFinder.isPathConstructed())
{ {
mMoveNow = false; moveNow = false;
mWalking = true; walking = true;
} }
} }
} }
if(mChooseAction) if(chooseAction)
{ {
mPlayedIdle = 0; playedIdle = 0;
getRandomIdle(); // NOTE: sets mPlayedIdle with a random selection getRandomIdle(playedIdle); // NOTE: sets mPlayedIdle with a random selection
if(!mPlayedIdle && mDistance) if(!playedIdle && mDistance)
{ {
mChooseAction = false; chooseAction = false;
mMoveNow = true; moveNow = true;
} }
else else
{ {
// Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander: // Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
MWWorld::TimeStamp currentTime = world->getTimeStamp(); MWWorld::TimeStamp currentTime = world->getTimeStamp();
mStartTime = currentTime; mStartTime = currentTime;
playIdle(actor, mPlayedIdle); playIdle(actor, playedIdle);
mChooseAction = false; chooseAction = false;
mIdleNow = true; idleNow = true;
// Play idle voiced dialogue entries randomly // Play idle voiced dialogue entries randomly
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
@ -426,7 +476,7 @@ namespace MWMechanics
} }
// Allow interrupting a walking actor to trigger a greeting // 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 // Play a random voice greeting if the player gets too close
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
@ -460,23 +510,21 @@ namespace MWMechanics
{ {
greetingTimer++; greetingTimer++;
if(mWalking) if(walking)
{ {
stopWalking(actor); stopWalking(actor);
mMoveNow = false; moveNow = false;
mWalking = false; walking = false;
mObstacleCheck.clear(); mObstacleCheck.clear();
mIdleNow = true; idleNow = true;
getRandomIdle(); getRandomIdle(playedIdle);
} }
if(!rotate) if(!rotate)
{ {
Ogre::Vector3 dir = playerPos - actorPos; Ogre::Vector3 dir = playerPos - actorPos;
float length = dir.length();
float faceAngle = Ogre::Radian(Ogre::Math::ACos(dir.y / length) * float faceAngle = Ogre::Math::ATan2(dir.x,dir.y).valueDegrees();
((Ogre::Math::ASin(dir.x / length).valueRadians()>0)?1.0:-1.0)).valueDegrees();
float actorAngle = actor.getRefData().getBaseNode()->getOrientation().getRoll().valueDegrees(); float actorAngle = actor.getRefData().getBaseNode()->getOrientation().getRoll().valueDegrees();
// an attempt at reducing the turning animation glitch // an attempt at reducing the turning animation glitch
if(abs(abs(faceAngle) - abs(actorAngle)) >= 5) // TODO: is there a better way? if(abs(abs(faceAngle) - abs(actorAngle)) >= 5) // TODO: is there a better way?
@ -503,15 +551,15 @@ namespace MWMechanics
} }
// Check if idle animation finished // 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; playedIdle = 0;
mIdleNow = false; idleNow = false;
mChooseAction = true; chooseAction = true;
} }
} }
if(mMoveNow && mDistance) if(moveNow && mDistance)
{ {
// Construct a new path if there isn't one // Construct a new path if there isn't one
if(!mPathFinder.isPathConstructed()) if(!mPathFinder.isPathConstructed())
@ -525,8 +573,8 @@ namespace MWMechanics
// convert dest to use world co-ordinates // convert dest to use world co-ordinates
ESM::Pathgrid::Point dest; ESM::Pathgrid::Point dest;
dest.mX = destNodePos[0] + cachedCellXf; dest.mX = destNodePos[0] + cachedCellXposition;
dest.mY = destNodePos[1] + cachedCellYf; dest.mY = destNodePos[1] + cachedCellYposition;
dest.mZ = destNodePos[2]; dest.mZ = destNodePos[2];
// actor position is already in world co-ordinates // actor position is already in world co-ordinates
@ -557,8 +605,8 @@ namespace MWMechanics
mAllowedNodes.push_back(mCurrentNode); mAllowedNodes.push_back(mCurrentNode);
mCurrentNode = temp; mCurrentNode = temp;
mMoveNow = false; moveNow = false;
mWalking = true; walking = true;
} }
// Choose a different node and delete this one from possible nodes because it is uncreachable: // Choose a different node and delete this one from possible nodes because it is uncreachable:
else else
@ -657,7 +705,7 @@ namespace MWMechanics
} }
} }
void AiWander::getRandomIdle() void AiWander::getRandomIdle(short unsigned& playedIdle)
{ {
unsigned short idleRoll = 0; unsigned short idleRoll = 0;
@ -670,7 +718,7 @@ namespace MWMechanics
unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / fIdleChanceMultiplier)); unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / fIdleChanceMultiplier));
if(randSelect < idleChance && randSelect > idleRoll) if(randSelect < idleChance && randSelect > idleRoll)
{ {
mPlayedIdle = counter+2; playedIdle = counter+2;
idleRoll = randSelect; idleRoll = randSelect;
} }
} }

@ -68,7 +68,7 @@ namespace MWMechanics
void stopWalking(const MWWorld::Ptr& actor); void stopWalking(const MWWorld::Ptr& actor);
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
bool checkIdle(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 mDistance; // how far the actor can wander from the spawn point
int mDuration; int mDuration;
@ -88,13 +88,9 @@ namespace MWMechanics
// if false triggers calculating allowed nodes based on mDistance // if false triggers calculating allowed nodes based on mDistance
bool mStoredAvailableNodes; bool mStoredAvailableNodes;
// AiWander states
bool mChooseAction;
bool mIdleNow;
bool mMoveNow;
bool mWalking;
unsigned short mPlayedIdle;
MWWorld::TimeStamp mStartTime; MWWorld::TimeStamp mStartTime;
@ -105,49 +101,16 @@ namespace MWMechanics
void trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes, void trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes,
const PathFinder& pathfinder); const PathFinder& pathfinder);
PathFinder mPathFinder; // PathFinder mPathFinder;
ObstacleCheck mObstacleCheck; // ObstacleCheck mObstacleCheck;
float mDoorCheckDuration; float mDoorCheckDuration;
int mStuckCount; 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<int>::max();
mCellY = std::numeric_limits<int>::max();
mXCell = 0;
mYCell = 0;
mCell = NULL;
};
};
} }
#endif #endif

Loading…
Cancel
Save