forked from mirror/openmw-tes3mp
Make stationary actors return to position on load
Also makes wandering actors resume their previous destination when an interrupting combat or pursuit ends. (Fixes #3656)
This commit is contained in:
parent
3356fb81ce
commit
4f08084e79
3 changed files with 49 additions and 61 deletions
|
@ -295,21 +295,6 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
||||||
if (actor == getPlayer())
|
if (actor == getPlayer())
|
||||||
throw std::runtime_error("Can't add AI packages to player");
|
throw std::runtime_error("Can't add AI packages to player");
|
||||||
|
|
||||||
if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPursue)
|
|
||||||
{
|
|
||||||
// Notify AiWander of our current position so we can return to it after combat finished
|
|
||||||
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
|
|
||||||
{
|
|
||||||
if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat
|
|
||||||
&& (*iter)->getTarget() == (&package)->getTarget())
|
|
||||||
{
|
|
||||||
return; // already in combat with this actor
|
|
||||||
}
|
|
||||||
else if ((*iter)->getTypeId() == AiPackage::TypeIdWander)
|
|
||||||
static_cast<AiWander*>(*iter)->setReturnPosition(actor.getRefData().getPosition().asVec3());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop combat when a non-combat AI package is added
|
// Stop combat when a non-combat AI package is added
|
||||||
if (isActualAiPackage(package.getTypeId()))
|
if (isActualAiPackage(package.getTypeId()))
|
||||||
stopCombat();
|
stopCombat();
|
||||||
|
|
|
@ -113,7 +113,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
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)
|
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0))
|
||||||
{
|
{
|
||||||
mIdle.resize(8, 0);
|
mIdle.resize(8, 0);
|
||||||
init();
|
init();
|
||||||
|
@ -123,9 +123,6 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
// NOTE: mDistance and mDuration must be set already
|
// NOTE: mDistance and mDuration must be set already
|
||||||
|
|
||||||
mHasReturnPosition = false;
|
|
||||||
mReturnPosition = osg::Vec3f(0,0,0);
|
|
||||||
|
|
||||||
if(mDistance < 0)
|
if(mDistance < 0)
|
||||||
mDistance = 0;
|
mDistance = 0;
|
||||||
if(mDuration < 0)
|
if(mDuration < 0)
|
||||||
|
@ -212,6 +209,19 @@ namespace MWMechanics
|
||||||
|
|
||||||
ESM::Position pos = actor.getRefData().getPosition();
|
ESM::Position pos = actor.getRefData().getPosition();
|
||||||
|
|
||||||
|
// If there is already a destination due to the package having been interrupted by a combat or pursue package,
|
||||||
|
// rebuild a path to it
|
||||||
|
if (!mPathFinder.isPathConstructed() && mHasDestination)
|
||||||
|
{
|
||||||
|
ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mDestination));
|
||||||
|
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
|
||||||
|
|
||||||
|
mPathFinder.buildSyncedPath(start, dest, actor.getCell());
|
||||||
|
|
||||||
|
if (mPathFinder.isPathConstructed())
|
||||||
|
storage.setState(Wander_Walking);
|
||||||
|
}
|
||||||
|
|
||||||
doPerFrameActionsForState(actor, duration, storage, pos);
|
doPerFrameActionsForState(actor, duration, storage, pos);
|
||||||
|
|
||||||
playIdleDialogueRandomly(actor);
|
playIdleDialogueRandomly(actor);
|
||||||
|
@ -253,9 +263,8 @@ namespace MWMechanics
|
||||||
getAllowedNodes(actor, currentCell->getCell(), storage);
|
getAllowedNodes(actor, currentCell->getCell(), storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actor becomes stationary - see above URL's for previous research
|
// If the package has a wander distance but no pathgrid is available,
|
||||||
// If a creature or an NPC with a wander distance and no pathgrid is available,
|
// randomly idle or wander near spawn point
|
||||||
// randomly idle or wander around near spawn point
|
|
||||||
if(storage.mAllowedNodes.empty() && mDistance > 0 && !storage.mIsWanderingManually) {
|
if(storage.mAllowedNodes.empty() && mDistance > 0 && !storage.mIsWanderingManually) {
|
||||||
// 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) >= 96) {
|
if (Misc::Rng::rollDice(100) >= 96) {
|
||||||
|
@ -277,10 +286,8 @@ namespace MWMechanics
|
||||||
mDistance = 0;
|
mDistance = 0;
|
||||||
|
|
||||||
// For stationary NPCs, move back to the starting location if another AiPackage moved us elsewhere
|
// For stationary NPCs, move back to the starting location if another AiPackage moved us elsewhere
|
||||||
if (cellChange)
|
if (mDistance == 0 && !cellChange
|
||||||
mHasReturnPosition = false;
|
&& (pos.asVec3() - mInitialActorPosition).length2() > (DESTINATION_TOLERANCE * DESTINATION_TOLERANCE))
|
||||||
if (mDistance == 0 && mHasReturnPosition
|
|
||||||
&& (pos.asVec3() - mReturnPosition).length2() > (DESTINATION_TOLERANCE * DESTINATION_TOLERANCE))
|
|
||||||
{
|
{
|
||||||
returnToStartLocation(actor, storage, pos);
|
returnToStartLocation(actor, storage, pos);
|
||||||
}
|
}
|
||||||
|
@ -334,7 +341,8 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
if (!mPathFinder.isPathConstructed())
|
if (!mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mReturnPosition));
|
mDestination = mInitialActorPosition;
|
||||||
|
ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mDestination));
|
||||||
|
|
||||||
// actor position is already in world coordinates
|
// actor position is already in world coordinates
|
||||||
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
|
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
|
||||||
|
@ -345,6 +353,7 @@ namespace MWMechanics
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
storage.setState(Wander_Walking);
|
storage.setState(Wander_Walking);
|
||||||
|
mHasDestination = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,7 +366,6 @@ namespace MWMechanics
|
||||||
const osg::Vec3f currentPositionVec3f = osg::Vec3f(currentPosition.mX, currentPosition.mY, currentPosition.mZ);
|
const osg::Vec3f currentPositionVec3f = osg::Vec3f(currentPosition.mX, currentPosition.mY, currentPosition.mZ);
|
||||||
|
|
||||||
std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here
|
std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here
|
||||||
osg::Vec3f destination;
|
|
||||||
ESM::Pathgrid::Point destinationPosition;
|
ESM::Pathgrid::Point destinationPosition;
|
||||||
bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
|
bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
|
||||||
do {
|
do {
|
||||||
|
@ -369,17 +377,18 @@ namespace MWMechanics
|
||||||
const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection);
|
const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection);
|
||||||
const float destinationZ = mInitialActorPosition.z();
|
const float destinationZ = mInitialActorPosition.z();
|
||||||
destinationPosition = ESM::Pathgrid::Point(destinationX, destinationY, destinationZ);
|
destinationPosition = ESM::Pathgrid::Point(destinationX, destinationY, destinationZ);
|
||||||
destination = osg::Vec3f(destinationX, destinationY, destinationZ);
|
mDestination = osg::Vec3f(destinationX, destinationY, destinationZ);
|
||||||
|
|
||||||
// Check if land creature will walk onto water or if water creature will swim onto land
|
// Check if land creature will walk onto water or if water creature will swim onto land
|
||||||
if ((!isWaterCreature && !destinationIsAtWater(actor, destination)) ||
|
if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) ||
|
||||||
(isWaterCreature && !destinationThroughGround(currentPositionVec3f, destination))) {
|
(isWaterCreature && !destinationThroughGround(currentPositionVec3f, mDestination))) {
|
||||||
mPathFinder.buildSyncedPath(currentPosition, destinationPosition, actor.getCell());
|
mPathFinder.buildSyncedPath(currentPosition, destinationPosition, actor.getCell());
|
||||||
mPathFinder.addPointToPath(destinationPosition);
|
mPathFinder.addPointToPath(destinationPosition);
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
storage.setState(Wander_Walking, true);
|
storage.setState(Wander_Walking, true);
|
||||||
|
mHasDestination = true;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -466,6 +475,9 @@ namespace MWMechanics
|
||||||
GreetingState& greetingState = storage.mSaidGreeting;
|
GreetingState& greetingState = storage.mSaidGreeting;
|
||||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
|
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
|
||||||
{
|
{
|
||||||
|
if (mPathFinder.isPathConstructed())
|
||||||
|
storage.setState(Wander_Walking);
|
||||||
|
else
|
||||||
storage.setState(Wander_ChooseAction);
|
storage.setState(Wander_ChooseAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -478,7 +490,6 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
stopWalking(actor, storage);
|
stopWalking(actor, storage);
|
||||||
storage.setState(Wander_ChooseAction);
|
storage.setState(Wander_ChooseAction);
|
||||||
mHasReturnPosition = false;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -490,8 +501,8 @@ namespace MWMechanics
|
||||||
void AiWander::onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
void AiWander::onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
|
|
||||||
short unsigned& idleAnimation = storage.mIdleAnimation;
|
unsigned short idleAnimation = getRandomIdle();
|
||||||
idleAnimation = getRandomIdle();
|
storage.mIdleAnimation = idleAnimation;
|
||||||
|
|
||||||
if (!idleAnimation && mDistance)
|
if (!idleAnimation && mDistance)
|
||||||
{
|
{
|
||||||
|
@ -525,7 +536,7 @@ namespace MWMechanics
|
||||||
storage.mTrimCurrentNode = true;
|
storage.mTrimCurrentNode = true;
|
||||||
trimAllowedNodes(storage.mAllowedNodes, mPathFinder);
|
trimAllowedNodes(storage.mAllowedNodes, mPathFinder);
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
mPathFinder.clearPath();
|
stopWalking(actor, storage);
|
||||||
storage.setState(Wander_MoveNow);
|
storage.setState(Wander_MoveNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,9 +546,7 @@ namespace MWMechanics
|
||||||
// if stuck for sufficiently long, act like current location was the destination
|
// if stuck for sufficiently long, act like current location was the destination
|
||||||
if (storage.mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
|
if (storage.mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
|
||||||
{
|
{
|
||||||
//std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl;
|
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
|
|
||||||
stopWalking(actor, storage);
|
stopWalking(actor, storage);
|
||||||
storage.setState(Wander_ChooseAction);
|
storage.setState(Wander_ChooseAction);
|
||||||
storage.mStuckCount = 0;
|
storage.mStuckCount = 0;
|
||||||
|
@ -612,7 +621,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (storage.mState == Wander_Walking)
|
if (storage.mState == Wander_Walking)
|
||||||
{
|
{
|
||||||
stopWalking(actor, storage);
|
stopWalking(actor, storage, false);
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
storage.setState(Wander_IdleNow);
|
storage.setState(Wander_IdleNow);
|
||||||
}
|
}
|
||||||
|
@ -657,6 +666,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
|
mDestination = osg::Vec3f(dest.mX, dest.mY, dest.mZ);
|
||||||
|
mHasDestination = true;
|
||||||
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
|
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
|
||||||
ESM::Pathgrid::Point temp = storage.mAllowedNodes[randNode];
|
ESM::Pathgrid::Point temp = storage.mAllowedNodes[randNode];
|
||||||
storage.mAllowedNodes.erase(storage.mAllowedNodes.begin() + randNode);
|
storage.mAllowedNodes.erase(storage.mAllowedNodes.begin() + randNode);
|
||||||
|
@ -710,9 +721,13 @@ namespace MWMechanics
|
||||||
return TypeIdWander;
|
return TypeIdWander;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage, bool clearPath)
|
||||||
|
{
|
||||||
|
if (clearPath)
|
||||||
{
|
{
|
||||||
mPathFinder.clearPath();
|
mPathFinder.clearPath();
|
||||||
|
mHasDestination = false;
|
||||||
|
}
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,15 +758,6 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::setReturnPosition(const osg::Vec3f& position)
|
|
||||||
{
|
|
||||||
if (!mHasReturnPosition)
|
|
||||||
{
|
|
||||||
mHasReturnPosition = true;
|
|
||||||
mReturnPosition = position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
short unsigned AiWander::getRandomIdle()
|
short unsigned AiWander::getRandomIdle()
|
||||||
{
|
{
|
||||||
unsigned short idleRoll = 0;
|
unsigned short idleRoll = 0;
|
||||||
|
@ -945,6 +951,8 @@ namespace MWMechanics
|
||||||
, mTimeOfDay(wander->mData.mTimeOfDay)
|
, mTimeOfDay(wander->mData.mTimeOfDay)
|
||||||
, mRepeat(wander->mData.mShouldRepeat != 0)
|
, mRepeat(wander->mData.mShouldRepeat != 0)
|
||||||
, mStoredInitialActorPosition(wander->mStoredInitialActorPosition)
|
, mStoredInitialActorPosition(wander->mStoredInitialActorPosition)
|
||||||
|
, mHasDestination(false)
|
||||||
|
, mDestination(osg::Vec3f(0, 0, 0))
|
||||||
{
|
{
|
||||||
if (mStoredInitialActorPosition)
|
if (mStoredInitialActorPosition)
|
||||||
mInitialActorPosition = wander->mInitialActorPosition;
|
mInitialActorPosition = wander->mInitialActorPosition;
|
||||||
|
@ -953,7 +961,6 @@ namespace MWMechanics
|
||||||
if (mRemainingDuration <= 0 || mRemainingDuration >= 24)
|
if (mRemainingDuration <= 0 || mRemainingDuration >= 24)
|
||||||
mRemainingDuration = mDuration;
|
mRemainingDuration = mDuration;
|
||||||
|
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,10 +44,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
virtual int getTypeId() const;
|
virtual int getTypeId() const;
|
||||||
|
|
||||||
/// Set the position to return to for a stationary (non-wandering) actor
|
|
||||||
/** In case another AI package moved the actor elsewhere **/
|
|
||||||
void setReturnPosition (const osg::Vec3f& position);
|
|
||||||
|
|
||||||
virtual void writeState(ESM::AiSequence::AiSequence &sequence) const;
|
virtual void writeState(ESM::AiSequence::AiSequence &sequence) const;
|
||||||
|
|
||||||
virtual void fastForward(const MWWorld::Ptr& actor, AiState& state);
|
virtual void fastForward(const MWWorld::Ptr& actor, AiState& state);
|
||||||
|
@ -70,7 +66,7 @@ namespace MWMechanics
|
||||||
private:
|
private:
|
||||||
// NOTE: mDistance and mDuration must be set already
|
// NOTE: mDistance and mDuration must be set already
|
||||||
void init();
|
void init();
|
||||||
void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage, bool clearPath = true);
|
||||||
|
|
||||||
/// Have the given actor play an idle animation
|
/// Have the given actor play an idle animation
|
||||||
/// @return Success or error
|
/// @return Success or error
|
||||||
|
@ -102,11 +98,11 @@ namespace MWMechanics
|
||||||
std::vector<unsigned char> mIdle;
|
std::vector<unsigned char> mIdle;
|
||||||
bool mRepeat;
|
bool mRepeat;
|
||||||
|
|
||||||
bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position,
|
|
||||||
// if we had the actor in the AiWander constructor...
|
|
||||||
osg::Vec3f mReturnPosition;
|
|
||||||
osg::Vec3f mInitialActorPosition;
|
|
||||||
bool mStoredInitialActorPosition;
|
bool mStoredInitialActorPosition;
|
||||||
|
osg::Vec3f mInitialActorPosition;
|
||||||
|
|
||||||
|
bool mHasDestination;
|
||||||
|
osg::Vec3f mDestination;
|
||||||
|
|
||||||
void getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell, AiWanderStorage& storage);
|
void getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell, AiWanderStorage& storage);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue