Merge remote-tracking branch 'dteviot/refactoringAiWander'

sceneinput
Marc Zinnschlag 10 years ago
commit d22f8703e5

@ -117,7 +117,7 @@ namespace MWMechanics
mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStoredAvailableNodes = false; mPopulateAvailableNodes = true;
} }
@ -191,7 +191,7 @@ namespace MWMechanics
if(!currentCell || cellChange) if(!currentCell || cellChange)
{ {
currentCell = actor.getCell(); currentCell = actor.getCell();
mStoredAvailableNodes = false; // prob. not needed since mDistance = 0 mPopulateAvailableNodes = true;
} }
cStats.setDrawState(DrawState_Nothing); cStats.setDrawState(DrawState_Nothing);
@ -225,8 +225,6 @@ namespace MWMechanics
storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE)) storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE))
{ {
stopWalking(actor, storage); stopWalking(actor, storage);
moveNow = false;
walking = false;
chooseAction = true; chooseAction = true;
mHasReturnPosition = false; mHasReturnPosition = false;
} }
@ -239,45 +237,7 @@ namespace MWMechanics
zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
// Returns true if evasive action needs to be taken evadeObstacles(actor, storage, duration);
if(mObstacleCheck.check(actor, duration))
{
// first check if we're walking into a door
if(proximityToDoor(actor)) // NOTE: checks interior cells only
{
// remove allowed points then select another random destination
mTrimCurrentNode = true;
trimAllowedNodes(mAllowedNodes, storage.mPathFinder);
mObstacleCheck.clear();
storage.mPathFinder.clearPath();
walking = false;
moveNow = true;
}
else // probably walking into another NPC
{
// TODO: diagonal should have same animation as walk forward
// but doesn't seem to do that?
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
// change the angle a bit, too
zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
}
mStuckCount++; // TODO: maybe no longer needed
}
//#if 0
// TODO: maybe no longer needed
if(mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
{
//std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl;
mObstacleCheck.clear();
stopWalking(actor, storage);
moveNow = false;
walking = false;
chooseAction = true;
mStuckCount = 0;
}
//#endif
} }
@ -325,32 +285,7 @@ namespace MWMechanics
} }
} }
// Play idle voiced dialogue entries randomly playIdleDialogueRandomly(actor);
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
if (hello > 0 && !MWBase::Environment::get().getWorld()->isSwimming(actor)
&& MWBase::Environment::get().getSoundManager()->sayDone(actor))
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
float roll = Misc::Rng::rollProbability() * 10000.0f;
// In vanilla MW the chance was FPS dependent, and did not allow proper changing of fVoiceIdleOdds
// due to the roll being an integer.
// Our implementation does not have these issues, so needs to be recalibrated. We chose to
// use the chance MW would have when run at 60 FPS with the default value of the GMST for calibration.
float x = fVoiceIdleOdds * 0.6f * (MWBase::Environment::get().getFrameDuration() / 0.1f);
// Only say Idle voices when player is in LOS
// A bit counterintuitive, likely vanilla did this to reduce the appearance of
// voices going through walls?
if (roll < x && (player.getRefData().getPosition().asVec3() - pos.asVec3()).length2()
< 3000*3000 // maybe should be fAudioVoiceDefaultMaxDistance*fAudioMaxDistanceMult instead
&& MWBase::Environment::get().getWorld()->getLOS(player, actor))
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
}
float& lastReaction = storage.mReaction; float& lastReaction = storage.mReaction;
lastReaction += duration; lastReaction += duration;
@ -367,17 +302,8 @@ namespace MWMechanics
{ {
// End package if duration is complete or mid-night hits: // End package if duration is complete or mid-night hits:
MWWorld::TimeStamp currentTime = world->getTimeStamp(); MWWorld::TimeStamp currentTime = world->getTimeStamp();
if(currentTime.getHour() >= mStartTime.getHour() + mDuration) if((currentTime.getHour() >= mStartTime.getHour() + mDuration) ||
{ (int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay()))
if(!mRepeat)
{
stopWalking(actor, storage);
return true;
}
else
mStartTime = currentTime;
}
else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay())
{ {
if(!mRepeat) if(!mRepeat)
{ {
@ -390,7 +316,7 @@ namespace MWMechanics
} }
// Initialization to discover & store allowed node points for this actor. // Initialization to discover & store allowed node points for this actor.
if(!mStoredAvailableNodes) if (mPopulateAvailableNodes)
{ {
getAllowedNodes(actor, currentCell->getCell()); getAllowedNodes(actor, currentCell->getCell());
} }
@ -431,9 +357,100 @@ namespace MWMechanics
// Allow interrupting a walking actor to trigger a greeting // Allow interrupting a walking actor to trigger a greeting
if(idleNow || walking) if(idleNow || walking)
{
playGreetingIfPlayerGetsTooClose(actor, storage);
}
if(moveNow && mDistance)
{
// Construct a new path if there isn't one
if(!storage.mPathFinder.isPathConstructed())
{
if (mAllowedNodes.size())
{
setPathToAnAllowedNode(actor, storage, pos);
}
}
}
return false; // AiWander package not yet completed
}
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration)
{
if (mObstacleCheck.check(actor, duration))
{
// first check if we're walking into a door
if (proximityToDoor(actor)) // NOTE: checks interior cells only
{
// remove allowed points then select another random destination
mTrimCurrentNode = true;
trimAllowedNodes(mAllowedNodes, storage.mPathFinder);
mObstacleCheck.clear();
storage.mPathFinder.clearPath();
storage.mWalking = false;
storage.mMoveNow = true;
}
else // probably walking into another NPC
{
// TODO: diagonal should have same animation as walk forward
// but doesn't seem to do that?
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
// change the angle a bit, too
const ESM::Position& pos = actor.getRefData().getPosition();
zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
}
mStuckCount++; // TODO: maybe no longer needed
}
//#if 0
// TODO: maybe no longer needed
if (mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
{
//std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl;
mObstacleCheck.clear();
stopWalking(actor, storage);
storage.mChooseAction = true;
mStuckCount = 0;
}
//#endif
}
void AiWander::playIdleDialogueRandomly(const MWWorld::Ptr& actor)
{
int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified();
if (hello > 0 && !MWBase::Environment::get().getWorld()->isSwimming(actor)
&& MWBase::Environment::get().getSoundManager()->sayDone(actor))
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
float roll = Misc::Rng::rollProbability() * 10000.0f;
// In vanilla MW the chance was FPS dependent, and did not allow proper changing of fVoiceIdleOdds
// due to the roll being an integer.
// Our implementation does not have these issues, so needs to be recalibrated. We chose to
// use the chance MW would have when run at 60 FPS with the default value of the GMST for calibration.
float x = fVoiceIdleOdds * 0.6f * (MWBase::Environment::get().getFrameDuration() / 0.1f);
// Only say Idle voices when player is in LOS
// A bit counterintuitive, likely vanilla did this to reduce the appearance of
// voices going through walls?
const ESM::Position& pos = actor.getRefData().getPosition();
if (roll < x && (player.getRefData().getPosition().asVec3() - pos.asVec3()).length2()
< 3000 * 3000 // maybe should be fAudioVoiceDefaultMaxDistance*fAudioMaxDistanceMult instead
&& MWBase::Environment::get().getWorld()->getLOS(player, actor))
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
}
}
void AiWander::playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage)
{ {
// 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 = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified();
float helloDistance = static_cast<float>(hello); float helloDistance = static_cast<float>(hello);
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore() static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->getInt(); .get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->getInt();
@ -446,6 +463,7 @@ namespace MWMechanics
float playerDistSqr = (playerPos - actorPos).length2(); float playerDistSqr = (playerPos - actorPos).length2();
int& greetingTimer = storage.mGreetingTimer; int& greetingTimer = storage.mGreetingTimer;
GreetingState& greetingState = storage.mSaidGreeting;
if (greetingState == Greet_None) if (greetingState == Greet_None)
{ {
if ((playerDistSqr <= helloDistance*helloDistance) && if ((playerDistSqr <= helloDistance*helloDistance) &&
@ -465,23 +483,21 @@ namespace MWMechanics
{ {
greetingTimer++; greetingTimer++;
if(walking) if (storage.mWalking)
{ {
stopWalking(actor, storage); stopWalking(actor, storage);
moveNow = false;
walking = false;
mObstacleCheck.clear(); mObstacleCheck.clear();
idleNow = true; storage.mIdleNow = true;
getRandomIdle(playedIdle); getRandomIdle(storage.mPlayedIdle);
} }
if(!rotate) if (!storage.mRotate)
{ {
osg::Vec3f dir = playerPos - actorPos; osg::Vec3f dir = playerPos - actorPos;
float faceAngleRadians = std::atan2(dir.x(), dir.y()); float faceAngleRadians = std::atan2(dir.x(), dir.y());
targetAngleRadians = faceAngleRadians; storage.mTargetAngleRadians = faceAngleRadians;
rotate = true; storage.mRotate = true;
} }
if (greetingTimer >= GREETING_SHOULD_END) if (greetingTimer >= GREETING_SHOULD_END)
@ -499,18 +515,14 @@ namespace MWMechanics
} }
} }
if(moveNow && mDistance) void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
{
// Construct a new path if there isn't one
if(!storage.mPathFinder.isPathConstructed())
{ {
assert(mAllowedNodes.size());
unsigned int randNode = Misc::Rng::rollDice(mAllowedNodes.size()); unsigned int randNode = Misc::Rng::rollDice(mAllowedNodes.size());
ESM::Pathgrid::Point dest(mAllowedNodes[randNode]); ESM::Pathgrid::Point dest(mAllowedNodes[randNode]);
ToWorldCoordinates(dest, currentCell->getCell()); ToWorldCoordinates(dest, storage.mCell->getCell());
// actor position is already in world co-ordinates // actor position is already in world co-ordinates
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actorPos));
// don't take shortcuts for wandering // don't take shortcuts for wandering
storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell(), false); storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell(), false);
@ -527,17 +539,13 @@ namespace MWMechanics
mAllowedNodes.push_back(mCurrentNode); mAllowedNodes.push_back(mCurrentNode);
mCurrentNode = temp; mCurrentNode = temp;
moveNow = false; storage.mMoveNow = false;
walking = true; storage.mWalking = 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
mAllowedNodes.erase(mAllowedNodes.begin() + randNode); mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
} }
}
return false; // AiWander package not yet completed
}
void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell) void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell)
{ {
@ -583,6 +591,8 @@ namespace MWMechanics
{ {
storage.mPathFinder.clearPath(); storage.mPathFinder.clearPath();
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
storage.mMoveNow = false;
storage.mWalking = false;
} }
void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)
@ -640,7 +650,7 @@ namespace MWMechanics
if (mDistance == 0) if (mDistance == 0)
return; return;
if (!mStoredAvailableNodes) if (mPopulateAvailableNodes)
getAllowedNodes(actor, actor.getCell()->getCell()); getAllowedNodes(actor, actor.getCell()->getCell());
if (mAllowedNodes.empty()) if (mAllowedNodes.empty())
@ -660,7 +670,7 @@ namespace MWMechanics
actor.getClass().adjustPosition(actor, false); actor.getClass().adjustPosition(actor, false);
// may have changed cell // may have changed cell
mStoredAvailableNodes = false; mPopulateAvailableNodes = true;
} }
int AiWander::OffsetToPreventOvercrowding() int AiWander::OffsetToPreventOvercrowding()
@ -722,8 +732,9 @@ namespace MWMechanics
{ {
SetCurrentNodeToClosestAllowedNode(npcPos); SetCurrentNodeToClosestAllowedNode(npcPos);
} }
mStoredAvailableNodes = true; // set only if successful in finding allowed nodes
} }
mPopulateAvailableNodes = false;
} }
// When only one path grid point in wander distance, // When only one path grid point in wander distance,

@ -71,6 +71,10 @@ namespace MWMechanics
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(unsigned short& playedIdle); void getRandomIdle(unsigned short& playedIdle);
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage);
void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration);
void playIdleDialogueRandomly(const MWWorld::Ptr& actor);
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,8 +92,8 @@ namespace MWMechanics
// if false triggers calculating allowed nodes based on mDistance // do we need to calculate allowed nodes based on mDistance
bool mStoredAvailableNodes; bool mPopulateAvailableNodes;

Loading…
Cancel
Save