Refactor AiTemporaryStorage usage

pull/457/head
Andrei Kortunov 7 years ago
parent 705b2dca0e
commit ee45f54b53

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

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

@ -1365,7 +1365,7 @@ namespace MWMechanics
{
CreatureStats &stats = iter->first.getClass().getCreatureStats(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())
continue;
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
seq.fastForward(ptr, it->second->getAiState());
seq.fastForward(ptr);
}
}
}

@ -35,74 +35,6 @@ namespace
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)
{
mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();

@ -23,7 +23,72 @@ namespace MWMechanics
{
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
class AiCombat : public AiPackage

@ -17,22 +17,6 @@
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;
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)

@ -19,6 +19,21 @@ namespace AiSequence
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
/** 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);
}
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())
{
@ -262,7 +262,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
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
if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat()))
@ -494,12 +494,12 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
mLastAiPackage = sequence.mLastAiPackage;
}
void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state)
void AiSequence::fastForward(const MWWorld::Ptr& actor)
{
if (!mPackages.empty())
{
MWMechanics::AiPackage* package = mPackages.front();
package->fastForward(actor, state);
package->fastForward(actor, mAiState);
}
}

@ -3,6 +3,8 @@
#include <list>
#include "aistate.hpp"
#include <components/esm/loadnpc.hpp>
namespace MWWorld
@ -47,6 +49,7 @@ namespace MWMechanics
/// The type of AI package that ran last
int mLastAiPackage;
AiState mAiState;
public:
///Default constructor
@ -104,10 +107,10 @@ namespace MWMechanics
void stopPursuit();
/// 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
void fastForward(const MWWorld::Ptr &actor, AiState &state);
void fastForward(const MWWorld::Ptr &actor);
/// Remove all packages.
void clear();

@ -51,67 +51,6 @@ namespace MWMechanics
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):
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))
@ -221,7 +160,7 @@ namespace MWMechanics
mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
if (mPathFinder.isPathConstructed())
storage.setState(Wander_Walking);
storage.setState(AiWanderStorage::Wander_Walking);
}
doPerFrameActionsForState(actor, duration, storage, pos);
@ -270,7 +209,7 @@ namespace MWMechanics
if(actorCanMoveByZ && mDistance > 0) {
// 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);
}
@ -283,7 +222,7 @@ namespace MWMechanics
if (Misc::Rng::rollDice(100) >= 96) {
wanderNearStart(actor, storage, mDistance);
} else {
storage.setState(Wander_IdleNow);
storage.setState(AiWanderStorage::Wander_IdleNow);
}
} else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) {
storage.mCanWanderAlongPathGrid = false;
@ -299,13 +238,13 @@ namespace MWMechanics
mDistance = 0;
// Allow interrupting a walking actor to trigger a greeting
WanderState& wanderState = storage.mState;
if ((wanderState == Wander_IdleNow) || (wanderState == Wander_Walking))
AiWanderStorage::WanderState& wanderState = storage.mState;
if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking))
{
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
if(!mPathFinder.isPathConstructed())
@ -381,7 +320,7 @@ namespace MWMechanics
if (mPathFinder.isPathConstructed())
{
storage.setState(Wander_Walking, true);
storage.setState(AiWanderStorage::Wander_Walking, true);
mHasDestination = true;
}
return;
@ -410,26 +349,26 @@ namespace MWMechanics
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
stopWalking(actor, storage);
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)
{
switch (storage.mState)
{
case Wander_IdleNow:
case AiWanderStorage::Wander_IdleNow:
onIdleStatePerFrameActions(actor, duration, storage);
break;
case Wander_Walking:
case AiWanderStorage::Wander_Walking:
onWalkingStatePerFrameActions(actor, duration, storage, pos);
break;
case Wander_ChooseAction:
case AiWanderStorage::Wander_ChooseAction:
onChooseActionStatePerFrameActions(actor, storage);
break;
case Wander_MoveNow:
case AiWanderStorage::Wander_MoveNow:
break; // nothing to do
default:
@ -451,7 +390,7 @@ namespace MWMechanics
if (mDistance && // actor is not intended to be stationary
proximityToDoor(actor, distance*1.6f))
{
storage.setState(Wander_MoveNow);
storage.setState(AiWanderStorage::Wander_MoveNow);
storage.mTrimCurrentNode = false; // just in case
return;
}
@ -468,13 +407,13 @@ namespace MWMechanics
}
// Check if idle animation finished
GreetingState& greetingState = storage.mSaidGreeting;
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None))
{
if (mPathFinder.isPathConstructed())
storage.setState(Wander_Walking);
storage.setState(AiWanderStorage::Wander_Walking);
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))
{
stopWalking(actor, storage);
storage.setState(Wander_ChooseAction);
storage.setState(AiWanderStorage::Wander_ChooseAction);
}
else
{
@ -502,7 +441,7 @@ namespace MWMechanics
if (!idleAnimation && mDistance)
{
storage.setState(Wander_MoveNow);
storage.setState(AiWanderStorage::Wander_MoveNow);
return;
}
if(idleAnimation)
@ -512,13 +451,13 @@ namespace MWMechanics
if(!playIdle(actor, idleAnimation))
{
storage.mBadIdles.push_back(idleAnimation);
storage.setState(Wander_ChooseAction);
storage.setState(AiWanderStorage::Wander_ChooseAction);
return;
}
}
}
storage.setState(Wander_IdleNow);
storage.setState(AiWanderStorage::Wander_IdleNow);
}
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
@ -534,7 +473,7 @@ namespace MWMechanics
trimAllowedNodes(storage.mAllowedNodes, mPathFinder);
mObstacleCheck.clear();
stopWalking(actor, storage);
storage.setState(Wander_MoveNow);
storage.setState(AiWanderStorage::Wander_MoveNow);
}
storage.mStuckCount++; // TODO: maybe no longer needed
@ -545,7 +484,7 @@ namespace MWMechanics
{
mObstacleCheck.clear();
stopWalking(actor, storage);
storage.setState(Wander_ChooseAction);
storage.setState(AiWanderStorage::Wander_ChooseAction);
storage.mStuckCount = 0;
}
}
@ -596,8 +535,8 @@ namespace MWMechanics
float playerDistSqr = (playerPos - actorPos).length2();
int& greetingTimer = storage.mGreetingTimer;
GreetingState& greetingState = storage.mSaidGreeting;
if (greetingState == Greet_None)
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
if (greetingState == AiWanderStorage::Greet_None)
{
if ((playerDistSqr <= helloDistance*helloDistance) &&
!player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor)
@ -606,37 +545,37 @@ namespace MWMechanics
if (greetingTimer >= GREETING_SHOULD_START)
{
greetingState = Greet_InProgress;
greetingState = AiWanderStorage::Greet_InProgress;
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
greetingTimer = 0;
}
}
if (greetingState == Greet_InProgress)
if (greetingState == AiWanderStorage::Greet_InProgress)
{
greetingTimer++;
if (storage.mState == Wander_Walking)
if (storage.mState == AiWanderStorage::Wander_Walking)
{
stopWalking(actor, storage, false);
mObstacleCheck.clear();
storage.setState(Wander_IdleNow);
storage.setState(AiWanderStorage::Wander_IdleNow);
}
turnActorToFacePlayer(actorPos, playerPos, storage);
if (greetingTimer >= GREETING_SHOULD_END)
{
greetingState = Greet_Done;
greetingState = AiWanderStorage::Greet_Done;
greetingTimer = 0;
}
}
if (greetingState == MWMechanics::AiWander::Greet_Done)
if (greetingState == AiWanderStorage::Greet_Done)
{
float resetDist = 2 * helloDistance;
if (playerDistSqr >= resetDist*resetDist)
greetingState = Greet_None;
greetingState = AiWanderStorage::Greet_None;
}
}
@ -676,7 +615,7 @@ namespace MWMechanics
storage.mAllowedNodes.push_back(storage.mCurrentNode);
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:
else

@ -22,7 +22,80 @@ namespace ESM
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
class AiWander : public AiPackage
@ -52,19 +125,6 @@ namespace MWMechanics
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:
// NOTE: mDistance and mDuration must be set already
void init();

Loading…
Cancel
Save