Merged pull request #1781

pull/457/head
Marc Zinnschlag 7 years ago
commit d9de8ccb5b

@ -50,6 +50,7 @@
Bug #4458: AiWander console command handles idle chances incorrectly Bug #4458: AiWander console command handles idle chances incorrectly
Bug #4459: NotCell dialogue condition doesn't support partial matches Bug #4459: NotCell dialogue condition doesn't support partial matches
Bug #4461: "Open" spell from non-player caster isn't a crime Bug #4461: "Open" spell from non-player caster isn't a crime
Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages
Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal
Bug #4474: No fallback when getVampireHead fails Bug #4474: No fallback when getVampireHead fails
Bug #4475: Scripted animations should not cause movement Bug #4475: Scripted animations should not cause movement

@ -4,7 +4,6 @@
namespace MWMechanics namespace MWMechanics
{ {
Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation) Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation)
{ {
mCharacterController.reset(new CharacterController(ptr, animation)); mCharacterController.reset(new CharacterController(ptr, animation));
@ -19,10 +18,4 @@ namespace MWMechanics
{ {
return mCharacterController.get(); return mCharacterController.get();
} }
AiState& Actor::getAiState()
{
return mAiState;
}
} }

@ -3,8 +3,6 @@
#include <memory> #include <memory>
#include "aistate.hpp"
namespace MWRender namespace MWRender
{ {
class Animation; class Animation;
@ -29,12 +27,8 @@ namespace MWMechanics
CharacterController* getCharacterController(); CharacterController* getCharacterController();
AiState& getAiState();
private: private:
std::unique_ptr<CharacterController> mCharacterController; std::unique_ptr<CharacterController> mCharacterController;
AiState mAiState;
}; };
} }

@ -1365,7 +1365,7 @@ namespace MWMechanics
{ {
CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
if (isConscious(iter->first)) if (isConscious(iter->first))
stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), iter->second->getAiState(), duration); stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), duration);
} }
} }
@ -1993,7 +1993,7 @@ namespace MWMechanics
|| ptr.getClass().getCreatureStats(ptr).isParalyzed()) || ptr.getClass().getCreatureStats(ptr).isParalyzed())
continue; continue;
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
seq.fastForward(ptr, it->second->getAiState()); seq.fastForward(ptr);
} }
} }
} }

@ -35,74 +35,6 @@ namespace
namespace MWMechanics namespace MWMechanics
{ {
/// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive.
struct AiCombatStorage : AiTemporaryBase
{
float mAttackCooldown;
float mTimerReact;
float mTimerCombatMove;
bool mReadyToAttack;
bool mAttack;
float mAttackRange;
bool mCombatMove;
osg::Vec3f mLastTargetPos;
const MWWorld::CellStore* mCell;
std::shared_ptr<Action> mCurrentAction;
float mActionCooldown;
float mStrength;
bool mForceNoShortcut;
ESM::Position mShortcutFailPos;
MWMechanics::Movement mMovement;
enum FleeState
{
FleeState_None,
FleeState_Idle,
FleeState_RunBlindly,
FleeState_RunToDestination
};
FleeState mFleeState;
bool mLOS;
float mUpdateLOSTimer;
float mFleeBlindRunTimer;
ESM::Pathgrid::Point mFleeDest;
AiCombatStorage():
mAttackCooldown(0.0f),
mTimerReact(AI_REACTION_TIME),
mTimerCombatMove(0.0f),
mReadyToAttack(false),
mAttack(false),
mAttackRange(0.0f),
mCombatMove(false),
mLastTargetPos(0,0,0),
mCell(NULL),
mCurrentAction(),
mActionCooldown(0.0f),
mStrength(),
mForceNoShortcut(false),
mShortcutFailPos(),
mMovement(),
mFleeState(FleeState_None),
mLOS(false),
mUpdateLOSTimer(0.0f),
mFleeBlindRunTimer(0.0f)
{}
void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
void updateCombatMove(float duration);
void stopCombatMove();
void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
const ESM::Weapon* weapon, bool distantCombat);
void updateAttack(CharacterController& characterController);
void stopAttack();
void startFleeing();
void stopFleeing();
bool isFleeing();
};
AiCombat::AiCombat(const MWWorld::Ptr& actor) AiCombat::AiCombat(const MWWorld::Ptr& actor)
{ {
mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId(); mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();
@ -115,7 +47,7 @@ namespace MWMechanics
void AiCombat::init() void AiCombat::init()
{ {
} }
/* /*

@ -23,7 +23,72 @@ namespace MWMechanics
{ {
class Action; class Action;
struct AiCombatStorage; /// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive.
struct AiCombatStorage : AiTemporaryBase
{
float mAttackCooldown;
float mTimerReact;
float mTimerCombatMove;
bool mReadyToAttack;
bool mAttack;
float mAttackRange;
bool mCombatMove;
osg::Vec3f mLastTargetPos;
const MWWorld::CellStore* mCell;
std::shared_ptr<Action> mCurrentAction;
float mActionCooldown;
float mStrength;
bool mForceNoShortcut;
ESM::Position mShortcutFailPos;
MWMechanics::Movement mMovement;
enum FleeState
{
FleeState_None,
FleeState_Idle,
FleeState_RunBlindly,
FleeState_RunToDestination
};
FleeState mFleeState;
bool mLOS;
float mUpdateLOSTimer;
float mFleeBlindRunTimer;
ESM::Pathgrid::Point mFleeDest;
AiCombatStorage():
mAttackCooldown(0.0f),
mTimerReact(AI_REACTION_TIME),
mTimerCombatMove(0.0f),
mReadyToAttack(false),
mAttack(false),
mAttackRange(0.0f),
mCombatMove(false),
mLastTargetPos(0,0,0),
mCell(NULL),
mCurrentAction(),
mActionCooldown(0.0f),
mStrength(),
mForceNoShortcut(false),
mShortcutFailPos(),
mMovement(),
mFleeState(FleeState_None),
mLOS(false),
mUpdateLOSTimer(0.0f),
mFleeBlindRunTimer(0.0f)
{}
void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
void updateCombatMove(float duration);
void stopCombatMove();
void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
const ESM::Weapon* weapon, bool distantCombat);
void updateAttack(CharacterController& characterController);
void stopAttack();
void startFleeing();
void stopFleeing();
bool isFleeing();
};
/// \brief Causes the actor to fight another actor /// \brief Causes the actor to fight another actor
class AiCombat : public AiPackage class AiCombat : public AiPackage

@ -17,22 +17,6 @@
namespace MWMechanics namespace MWMechanics
{ {
struct AiFollowStorage : AiTemporaryBase
{
float mTimer;
bool mMoving;
float mTargetAngleRadians;
bool mTurnActorToTarget;
AiFollowStorage() :
mTimer(0.f),
mMoving(false),
mTargetAngleRadians(0.f),
mTurnActorToTarget(false)
{}
};
int AiFollow::mFollowIndexCounter = 0; int AiFollow::mFollowIndexCounter = 0;
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z) AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)

@ -19,6 +19,21 @@ namespace AiSequence
namespace MWMechanics namespace MWMechanics
{ {
struct AiFollowStorage : AiTemporaryBase
{
float mTimer;
bool mMoving;
float mTargetAngleRadians;
bool mTurnActorToTarget;
AiFollowStorage() :
mTimer(0.f),
mMoving(false),
mTargetAngleRadians(0.f),
mTurnActorToTarget(false)
{}
};
/// \brief AiPackage for an actor to follow another actor/the PC /// \brief AiPackage for an actor to follow another actor/the PC
/** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely
**/ **/

@ -188,7 +188,7 @@ bool isActualAiPackage(int packageTypeId)
&& packageTypeId != AiPackage::TypeIdInternalTravel); && packageTypeId != AiPackage::TypeIdInternalTravel);
} }
void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration)
{ {
if(actor != getPlayer()) if(actor != getPlayer())
{ {
@ -262,7 +262,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
try try
{ {
if (package->execute (actor,characterController,state,duration)) if (package->execute (actor, characterController, mAiState, duration))
{ {
// Put repeating noncombat AI packages on the end of the stack so they can be used again // Put repeating noncombat AI packages on the end of the stack so they can be used again
if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat())) if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat()))
@ -360,6 +360,14 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
} }
mPackages.push_back (package.clone()); mPackages.push_back (package.clone());
// Make sure that temporary storage is empty
if (cancelOther)
{
mAiState.moveIn(new AiCombatStorage());
mAiState.moveIn(new AiFollowStorage());
mAiState.moveIn(new AiWanderStorage());
}
} }
AiPackage* MWMechanics::AiSequence::getActivePackage() AiPackage* MWMechanics::AiSequence::getActivePackage()
@ -494,12 +502,12 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
mLastAiPackage = sequence.mLastAiPackage; mLastAiPackage = sequence.mLastAiPackage;
} }
void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state) void AiSequence::fastForward(const MWWorld::Ptr& actor)
{ {
if (!mPackages.empty()) if (!mPackages.empty())
{ {
MWMechanics::AiPackage* package = mPackages.front(); MWMechanics::AiPackage* package = mPackages.front();
package->fastForward(actor, state); package->fastForward(actor, mAiState);
} }
} }

@ -3,6 +3,8 @@
#include <list> #include <list>
#include "aistate.hpp"
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
namespace MWWorld namespace MWWorld
@ -47,6 +49,7 @@ namespace MWMechanics
/// The type of AI package that ran last /// The type of AI package that ran last
int mLastAiPackage; int mLastAiPackage;
AiState mAiState;
public: public:
///Default constructor ///Default constructor
@ -104,10 +107,10 @@ namespace MWMechanics
void stopPursuit(); void stopPursuit();
/// Execute current package, switching if needed. /// Execute current package, switching if needed.
void execute (const MWWorld::Ptr& actor, CharacterController& characterController, MWMechanics::AiState& state, float duration); void execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration);
/// Simulate the passing of time using the currently active AI package /// Simulate the passing of time using the currently active AI package
void fastForward(const MWWorld::Ptr &actor, AiState &state); void fastForward(const MWWorld::Ptr &actor);
/// Remove all packages. /// Remove all packages.
void clear(); void clear();

@ -51,67 +51,6 @@ namespace MWMechanics
std::string("idle9"), std::string("idle9"),
}; };
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
struct AiWanderStorage : AiTemporaryBase
{
// the z rotation angle to reach
// when mTurnActorGivingGreetingToFacePlayer is true
float mTargetAngleRadians;
bool mTurnActorGivingGreetingToFacePlayer;
float mReaction; // update some actions infrequently
AiWander::GreetingState mSaidGreeting;
int mGreetingTimer;
const MWWorld::CellStore* mCell; // for detecting cell change
// AiWander states
AiWander::WanderState mState;
bool mIsWanderingManually;
bool mCanWanderAlongPathGrid;
unsigned short mIdleAnimation;
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
// do we need to calculate allowed nodes based on mDistance
bool mPopulateAvailableNodes;
// allowed pathgrid nodes based on mDistance from the spawn point
// in local coordinates of mCell
std::vector<ESM::Pathgrid::Point> mAllowedNodes;
ESM::Pathgrid::Point mCurrentNode;
bool mTrimCurrentNode;
float mDoorCheckDuration;
int mStuckCount;
AiWanderStorage():
mTargetAngleRadians(0),
mTurnActorGivingGreetingToFacePlayer(false),
mReaction(0),
mSaidGreeting(AiWander::Greet_None),
mGreetingTimer(0),
mCell(NULL),
mState(AiWander::Wander_ChooseAction),
mIsWanderingManually(false),
mCanWanderAlongPathGrid(true),
mIdleAnimation(0),
mBadIdles(),
mPopulateAvailableNodes(true),
mAllowedNodes(),
mTrimCurrentNode(false),
mDoorCheckDuration(0), // TODO: maybe no longer needed
mStuckCount(0)
{};
void setState(const AiWander::WanderState wanderState, const bool isManualWander = false) {
mState = wanderState;
mIsWanderingManually = isManualWander;
}
};
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), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle),
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)) mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0))
@ -221,7 +160,7 @@ namespace MWMechanics
mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell())); mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
storage.setState(Wander_Walking); storage.setState(AiWanderStorage::Wander_Walking);
} }
doPerFrameActionsForState(actor, duration, storage, pos); doPerFrameActionsForState(actor, duration, storage, pos);
@ -270,7 +209,7 @@ namespace MWMechanics
if(actorCanMoveByZ && mDistance > 0) { if(actorCanMoveByZ && mDistance > 0) {
// Typically want to idle for a short time before the next wander // Typically want to idle for a short time before the next wander
if (Misc::Rng::rollDice(100) >= 92 && storage.mState != Wander_Walking) { if (Misc::Rng::rollDice(100) >= 92 && storage.mState != AiWanderStorage::Wander_Walking) {
wanderNearStart(actor, storage, mDistance); wanderNearStart(actor, storage, mDistance);
} }
@ -283,7 +222,7 @@ namespace MWMechanics
if (Misc::Rng::rollDice(100) >= 96) { if (Misc::Rng::rollDice(100) >= 96) {
wanderNearStart(actor, storage, mDistance); wanderNearStart(actor, storage, mDistance);
} else { } else {
storage.setState(Wander_IdleNow); storage.setState(AiWanderStorage::Wander_IdleNow);
} }
} else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) { } else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) {
storage.mCanWanderAlongPathGrid = false; storage.mCanWanderAlongPathGrid = false;
@ -299,13 +238,13 @@ namespace MWMechanics
mDistance = 0; mDistance = 0;
// Allow interrupting a walking actor to trigger a greeting // Allow interrupting a walking actor to trigger a greeting
WanderState& wanderState = storage.mState; AiWanderStorage::WanderState& wanderState = storage.mState;
if ((wanderState == Wander_IdleNow) || (wanderState == Wander_Walking)) if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking))
{ {
playGreetingIfPlayerGetsTooClose(actor, storage); playGreetingIfPlayerGetsTooClose(actor, storage);
} }
if ((wanderState == Wander_MoveNow) && storage.mCanWanderAlongPathGrid) if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
{ {
// Construct a new path if there isn't one // Construct a new path if there isn't one
if(!mPathFinder.isPathConstructed()) if(!mPathFinder.isPathConstructed())
@ -381,7 +320,7 @@ namespace MWMechanics
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
{ {
storage.setState(Wander_Walking, true); storage.setState(AiWanderStorage::Wander_Walking, true);
mHasDestination = true; mHasDestination = true;
} }
return; return;
@ -410,26 +349,26 @@ namespace MWMechanics
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) { void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
stopWalking(actor, storage); stopWalking(actor, storage);
mObstacleCheck.clear(); mObstacleCheck.clear();
storage.setState(Wander_IdleNow); storage.setState(AiWanderStorage::Wander_IdleNow);
} }
void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos) void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos)
{ {
switch (storage.mState) switch (storage.mState)
{ {
case Wander_IdleNow: case AiWanderStorage::Wander_IdleNow:
onIdleStatePerFrameActions(actor, duration, storage); onIdleStatePerFrameActions(actor, duration, storage);
break; break;
case Wander_Walking: case AiWanderStorage::Wander_Walking:
onWalkingStatePerFrameActions(actor, duration, storage, pos); onWalkingStatePerFrameActions(actor, duration, storage, pos);
break; break;
case Wander_ChooseAction: case AiWanderStorage::Wander_ChooseAction:
onChooseActionStatePerFrameActions(actor, storage); onChooseActionStatePerFrameActions(actor, storage);
break; break;
case Wander_MoveNow: case AiWanderStorage::Wander_MoveNow:
break; // nothing to do break; // nothing to do
default: default:
@ -451,7 +390,7 @@ namespace MWMechanics
if (mDistance && // actor is not intended to be stationary if (mDistance && // actor is not intended to be stationary
proximityToDoor(actor, distance*1.6f)) proximityToDoor(actor, distance*1.6f))
{ {
storage.setState(Wander_MoveNow); storage.setState(AiWanderStorage::Wander_MoveNow);
storage.mTrimCurrentNode = false; // just in case storage.mTrimCurrentNode = false; // just in case
return; return;
} }
@ -468,13 +407,13 @@ namespace MWMechanics
} }
// Check if idle animation finished // Check if idle animation finished
GreetingState& greetingState = storage.mSaidGreeting; AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None)) if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None))
{ {
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
storage.setState(Wander_Walking); storage.setState(AiWanderStorage::Wander_Walking);
else else
storage.setState(Wander_ChooseAction); storage.setState(AiWanderStorage::Wander_ChooseAction);
} }
} }
@ -485,7 +424,7 @@ namespace MWMechanics
if ((!mPathFinder.isPathConstructed()) || pathTo(actor, ESM::Pathgrid::Point(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE)) if ((!mPathFinder.isPathConstructed()) || pathTo(actor, ESM::Pathgrid::Point(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE))
{ {
stopWalking(actor, storage); stopWalking(actor, storage);
storage.setState(Wander_ChooseAction); storage.setState(AiWanderStorage::Wander_ChooseAction);
} }
else else
{ {
@ -502,7 +441,7 @@ namespace MWMechanics
if (!idleAnimation && mDistance) if (!idleAnimation && mDistance)
{ {
storage.setState(Wander_MoveNow); storage.setState(AiWanderStorage::Wander_MoveNow);
return; return;
} }
if(idleAnimation) if(idleAnimation)
@ -512,13 +451,13 @@ namespace MWMechanics
if(!playIdle(actor, idleAnimation)) if(!playIdle(actor, idleAnimation))
{ {
storage.mBadIdles.push_back(idleAnimation); storage.mBadIdles.push_back(idleAnimation);
storage.setState(Wander_ChooseAction); storage.setState(AiWanderStorage::Wander_ChooseAction);
return; return;
} }
} }
} }
storage.setState(Wander_IdleNow); storage.setState(AiWanderStorage::Wander_IdleNow);
} }
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos) void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
@ -534,7 +473,7 @@ namespace MWMechanics
trimAllowedNodes(storage.mAllowedNodes, mPathFinder); trimAllowedNodes(storage.mAllowedNodes, mPathFinder);
mObstacleCheck.clear(); mObstacleCheck.clear();
stopWalking(actor, storage); stopWalking(actor, storage);
storage.setState(Wander_MoveNow); storage.setState(AiWanderStorage::Wander_MoveNow);
} }
storage.mStuckCount++; // TODO: maybe no longer needed storage.mStuckCount++; // TODO: maybe no longer needed
@ -545,7 +484,7 @@ namespace MWMechanics
{ {
mObstacleCheck.clear(); mObstacleCheck.clear();
stopWalking(actor, storage); stopWalking(actor, storage);
storage.setState(Wander_ChooseAction); storage.setState(AiWanderStorage::Wander_ChooseAction);
storage.mStuckCount = 0; storage.mStuckCount = 0;
} }
} }
@ -596,8 +535,8 @@ namespace MWMechanics
float playerDistSqr = (playerPos - actorPos).length2(); float playerDistSqr = (playerPos - actorPos).length2();
int& greetingTimer = storage.mGreetingTimer; int& greetingTimer = storage.mGreetingTimer;
GreetingState& greetingState = storage.mSaidGreeting; AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
if (greetingState == Greet_None) if (greetingState == AiWanderStorage::Greet_None)
{ {
if ((playerDistSqr <= helloDistance*helloDistance) && if ((playerDistSqr <= helloDistance*helloDistance) &&
!player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor) !player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor)
@ -606,37 +545,37 @@ namespace MWMechanics
if (greetingTimer >= GREETING_SHOULD_START) if (greetingTimer >= GREETING_SHOULD_START)
{ {
greetingState = Greet_InProgress; greetingState = AiWanderStorage::Greet_InProgress;
MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
greetingTimer = 0; greetingTimer = 0;
} }
} }
if (greetingState == Greet_InProgress) if (greetingState == AiWanderStorage::Greet_InProgress)
{ {
greetingTimer++; greetingTimer++;
if (storage.mState == Wander_Walking) if (storage.mState == AiWanderStorage::Wander_Walking)
{ {
stopWalking(actor, storage, false); stopWalking(actor, storage, false);
mObstacleCheck.clear(); mObstacleCheck.clear();
storage.setState(Wander_IdleNow); storage.setState(AiWanderStorage::Wander_IdleNow);
} }
turnActorToFacePlayer(actorPos, playerPos, storage); turnActorToFacePlayer(actorPos, playerPos, storage);
if (greetingTimer >= GREETING_SHOULD_END) if (greetingTimer >= GREETING_SHOULD_END)
{ {
greetingState = Greet_Done; greetingState = AiWanderStorage::Greet_Done;
greetingTimer = 0; greetingTimer = 0;
} }
} }
if (greetingState == MWMechanics::AiWander::Greet_Done) if (greetingState == AiWanderStorage::Greet_Done)
{ {
float resetDist = 2 * helloDistance; float resetDist = 2 * helloDistance;
if (playerDistSqr >= resetDist*resetDist) if (playerDistSqr >= resetDist*resetDist)
greetingState = Greet_None; greetingState = AiWanderStorage::Greet_None;
} }
} }
@ -676,7 +615,7 @@ namespace MWMechanics
storage.mAllowedNodes.push_back(storage.mCurrentNode); storage.mAllowedNodes.push_back(storage.mCurrentNode);
storage.mCurrentNode = temp; storage.mCurrentNode = temp;
storage.setState(Wander_Walking); storage.setState(AiWanderStorage::Wander_Walking);
} }
// 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

@ -21,8 +21,81 @@ namespace ESM
} }
namespace MWMechanics namespace MWMechanics
{ {
struct AiWanderStorage; /// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
struct AiWanderStorage : AiTemporaryBase
{
// the z rotation angle to reach
// when mTurnActorGivingGreetingToFacePlayer is true
float mTargetAngleRadians;
bool mTurnActorGivingGreetingToFacePlayer;
float mReaction; // update some actions infrequently
enum GreetingState
{
Greet_None,
Greet_InProgress,
Greet_Done
};
GreetingState mSaidGreeting;
int mGreetingTimer;
const MWWorld::CellStore* mCell; // for detecting cell change
// AiWander states
enum WanderState
{
Wander_ChooseAction,
Wander_IdleNow,
Wander_MoveNow,
Wander_Walking
};
WanderState mState;
bool mIsWanderingManually;
bool mCanWanderAlongPathGrid;
unsigned short mIdleAnimation;
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
// do we need to calculate allowed nodes based on mDistance
bool mPopulateAvailableNodes;
// allowed pathgrid nodes based on mDistance from the spawn point
// in local coordinates of mCell
std::vector<ESM::Pathgrid::Point> mAllowedNodes;
ESM::Pathgrid::Point mCurrentNode;
bool mTrimCurrentNode;
float mDoorCheckDuration;
int mStuckCount;
AiWanderStorage():
mTargetAngleRadians(0),
mTurnActorGivingGreetingToFacePlayer(false),
mReaction(0),
mSaidGreeting(Greet_None),
mGreetingTimer(0),
mCell(NULL),
mState(Wander_ChooseAction),
mIsWanderingManually(false),
mCanWanderAlongPathGrid(true),
mIdleAnimation(0),
mBadIdles(),
mPopulateAvailableNodes(true),
mAllowedNodes(),
mTrimCurrentNode(false),
mDoorCheckDuration(0), // TODO: maybe no longer needed
mStuckCount(0)
{};
void setState(const WanderState wanderState, const bool isManualWander = false)
{
mState = wanderState;
mIsWanderingManually = isManualWander;
}
};
/// \brief Causes the Actor to wander within a specified range /// \brief Causes the Actor to wander within a specified range
class AiWander : public AiPackage class AiWander : public AiPackage
@ -52,19 +125,6 @@ namespace MWMechanics
osg::Vec3f getDestination(const MWWorld::Ptr& actor) const; osg::Vec3f getDestination(const MWWorld::Ptr& actor) const;
enum GreetingState {
Greet_None,
Greet_InProgress,
Greet_Done
};
enum WanderState {
Wander_ChooseAction,
Wander_IdleNow,
Wander_MoveNow,
Wander_Walking
};
private: private:
// NOTE: mDistance and mDuration must be set already // NOTE: mDistance and mDuration must be set already
void init(); void init();

Loading…
Cancel
Save