mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-25 17:11:34 +00:00
Added reaction time and moved game setting variables init to the constructor.
This commit is contained in:
parent
3dfd08cf2d
commit
9bd31b6236
2 changed files with 101 additions and 77 deletions
|
@ -30,6 +30,10 @@ namespace MWMechanics
|
||||||
, mStuckCount(0) // TODO: maybe no longer needed
|
, mStuckCount(0) // TODO: maybe no longer needed
|
||||||
, mDoorCheckDuration(0)
|
, mDoorCheckDuration(0)
|
||||||
, mTrimCurrentNode(false)
|
, mTrimCurrentNode(false)
|
||||||
|
, mReaction(0)
|
||||||
|
, mGreetDistanceMultiplier(0)
|
||||||
|
, mGreetDistanceReset(0)
|
||||||
|
, mChance(0)
|
||||||
, mSaidGreeting(false)
|
, mSaidGreeting(false)
|
||||||
{
|
{
|
||||||
for(unsigned short counter = 0; counter < mIdle.size(); counter++)
|
for(unsigned short counter = 0; counter < mIdle.size(); counter++)
|
||||||
|
@ -47,8 +51,15 @@ namespace MWMechanics
|
||||||
|
|
||||||
mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
|
mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||||
mPlayedIdle = 0;
|
mPlayedIdle = 0;
|
||||||
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
mIdleChanceMultiplier =
|
mIdleChanceMultiplier =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fIdleChanceMultiplier")->getFloat();
|
store.get<ESM::GameSetting>().find("fIdleChanceMultiplier")->getFloat();
|
||||||
|
|
||||||
|
mGreetDistanceMultiplier =
|
||||||
|
store.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->getInt();
|
||||||
|
mGreetDistanceReset =
|
||||||
|
store.get<ESM::GameSetting>().find("fGreetDistanceReset")->getFloat();
|
||||||
|
mChance = store.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
|
||||||
|
|
||||||
mStoredAvailableNodes = false;
|
mStoredAvailableNodes = false;
|
||||||
mChooseAction = true;
|
mChooseAction = true;
|
||||||
|
@ -99,7 +110,7 @@ namespace MWMechanics
|
||||||
* | player) |
|
* | player) |
|
||||||
* +----------------------------------+
|
* +----------------------------------+
|
||||||
*
|
*
|
||||||
* TODO: non-time critical operations should be run once every 250ms or so.
|
* NOTE: non-time critical operations are run once every 250ms or so.
|
||||||
*
|
*
|
||||||
* TODO: It would be great if door opening/closing can be detected and pathgrid
|
* TODO: It would be great if door opening/closing can be detected and pathgrid
|
||||||
* links dynamically updated. Currently (0.29.0) AiWander allows choosing a
|
* links dynamically updated. Currently (0.29.0) AiWander allows choosing a
|
||||||
|
@ -128,6 +139,83 @@ namespace MWMechanics
|
||||||
|
|
||||||
cStats.setDrawState(DrawState_Nothing);
|
cStats.setDrawState(DrawState_Nothing);
|
||||||
cStats.setMovementFlag(CreatureStats::Flag_Run, false);
|
cStats.setMovementFlag(CreatureStats::Flag_Run, false);
|
||||||
|
|
||||||
|
ESM::Position pos = actor.getRefData().getPosition();
|
||||||
|
|
||||||
|
// Check if an idle actor is too close to a door - if so start walking
|
||||||
|
mDoorCheckDuration += duration;
|
||||||
|
if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL)
|
||||||
|
{
|
||||||
|
mDoorCheckDuration = 0; // restart timer
|
||||||
|
if(mDistance && // actor is not intended to be stationary
|
||||||
|
mIdleNow && // but is in idle
|
||||||
|
!mWalking && // FIXME: some actors are idle while walking
|
||||||
|
proximityToDoor(actor)) // NOTE: checks interior cells only
|
||||||
|
{
|
||||||
|
mIdleNow = false;
|
||||||
|
mMoveNow = true;
|
||||||
|
mTrimCurrentNode = false; // just in case
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mWalking) // have not yet reached the destination
|
||||||
|
{
|
||||||
|
// turn towards the next point in mPath
|
||||||
|
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||||
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
||||||
|
|
||||||
|
// Returns true if evasive action needs to be taken
|
||||||
|
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, mPathFinder);
|
||||||
|
mObstacleCheck.clear();
|
||||||
|
mPathFinder.clearPath();
|
||||||
|
mWalking = false;
|
||||||
|
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
|
||||||
|
zTurn(actor, Ogre::Degree(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);
|
||||||
|
mMoveNow = false;
|
||||||
|
mWalking = false;
|
||||||
|
mChooseAction = true;
|
||||||
|
}
|
||||||
|
//#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||||
|
|
||||||
|
mReaction += duration;
|
||||||
|
if(mReaction > 0.25f) // FIXME: hard coded constant
|
||||||
|
{
|
||||||
|
mReaction = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: everything below get updated every mReaction
|
||||||
|
|
||||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
if(mDuration)
|
if(mDuration)
|
||||||
{
|
{
|
||||||
|
@ -155,8 +243,6 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::Position pos = actor.getRefData().getPosition();
|
|
||||||
|
|
||||||
// Initialization to discover & store allowed node points for this actor.
|
// Initialization to discover & store allowed node points for this actor.
|
||||||
if(!mStoredAvailableNodes)
|
if(!mStoredAvailableNodes)
|
||||||
{
|
{
|
||||||
|
@ -270,50 +356,29 @@ namespace MWMechanics
|
||||||
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||||
if (hello > 0)
|
if (hello > 0)
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
|
||||||
float chance = store.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
|
|
||||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||||
|
|
||||||
// Don't bother if the player is out of hearing range
|
// Don't bother if the player is out of hearing range
|
||||||
if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(pos.pos)) < 1500)
|
if (roll < mChance && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500)
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if an idle actor is too close to a door - if so start walking
|
|
||||||
mDoorCheckDuration += duration;
|
|
||||||
if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL)
|
|
||||||
{
|
|
||||||
mDoorCheckDuration = 0; // restart timer
|
|
||||||
if(mDistance && // actor is not intended to be stationary
|
|
||||||
mIdleNow && // but is in idle
|
|
||||||
!mWalking && // FIXME: some actors are idle while walking
|
|
||||||
proximityToDoor(actor)) // NOTE: checks interior cells only
|
|
||||||
{
|
|
||||||
mIdleNow = false;
|
|
||||||
mMoveNow = true;
|
|
||||||
mTrimCurrentNode = false; // just in case
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow interrupting a walking actor to trigger a greeting
|
// Allow interrupting a walking actor to trigger a greeting
|
||||||
if(mIdleNow || (mWalking && !mObstacleCheck.isNormalState()))
|
if(mIdleNow || (mWalking && !mObstacleCheck.isNormalState()))
|
||||||
{
|
{
|
||||||
// Play a random voice greeting if the player gets too close
|
// Play a random voice greeting if the player gets too close
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
|
||||||
|
|
||||||
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||||
float helloDistance = hello;
|
float helloDistance = hello;
|
||||||
int iGreetDistanceMultiplier = store.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->getInt();
|
helloDistance *= mGreetDistanceMultiplier;
|
||||||
helloDistance *= iGreetDistanceMultiplier;
|
|
||||||
|
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||||
float playerDist = Ogre::Vector3(player.getRefData().getPosition().pos).distance(
|
float playerDist = Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(
|
||||||
Ogre::Vector3(actor.getRefData().getPosition().pos));
|
Ogre::Vector3(actor.getRefData().getPosition().pos));
|
||||||
|
|
||||||
if(mWalking && playerDist <= helloDistance)
|
if(mWalking && playerDist <= helloDistance*helloDistance)
|
||||||
{
|
{
|
||||||
stopWalking(actor);
|
stopWalking(actor);
|
||||||
mMoveNow = false;
|
mMoveNow = false;
|
||||||
|
@ -324,7 +389,7 @@ namespace MWMechanics
|
||||||
if (!mSaidGreeting)
|
if (!mSaidGreeting)
|
||||||
{
|
{
|
||||||
// TODO: check if actor is aware / has line of sight
|
// TODO: check if actor is aware / has line of sight
|
||||||
if (playerDist <= helloDistance
|
if (playerDist <= helloDistance*helloDistance
|
||||||
// Only play a greeting if the player is not moving
|
// Only play a greeting if the player is not moving
|
||||||
&& Ogre::Vector3(player.getClass().getMovementSettings(player).mPosition).squaredLength() == 0)
|
&& Ogre::Vector3(player.getClass().getMovementSettings(player).mPosition).squaredLength() == 0)
|
||||||
{
|
{
|
||||||
|
@ -335,8 +400,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
float fGreetDistanceReset = store.get<ESM::GameSetting>().find("fGreetDistanceReset")->getFloat();
|
if (playerDist >= mGreetDistanceReset*mGreetDistanceReset * mGreetDistanceMultiplier*mGreetDistanceMultiplier)
|
||||||
if (playerDist >= fGreetDistanceReset * iGreetDistanceMultiplier)
|
|
||||||
mSaidGreeting = false;
|
mSaidGreeting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,51 +477,6 @@ namespace MWMechanics
|
||||||
mWalking = false;
|
mWalking = false;
|
||||||
mChooseAction = true;
|
mChooseAction = true;
|
||||||
}
|
}
|
||||||
else if(mWalking) // have not yet reached the destination
|
|
||||||
{
|
|
||||||
// turn towards the next point in mPath
|
|
||||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
|
||||||
|
|
||||||
// Returns true if evasive action needs to be taken
|
|
||||||
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, mPathFinder);
|
|
||||||
mObstacleCheck.clear();
|
|
||||||
mPathFinder.clearPath();
|
|
||||||
mWalking = false;
|
|
||||||
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
|
|
||||||
zTurn(actor, Ogre::Degree(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);
|
|
||||||
mMoveNow = false;
|
|
||||||
mWalking = false;
|
|
||||||
mChooseAction = true;
|
|
||||||
}
|
|
||||||
//#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return false; // AiWander package not yet completed
|
return false; // AiWander package not yet completed
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,9 @@ namespace MWMechanics
|
||||||
bool mRepeat;
|
bool mRepeat;
|
||||||
|
|
||||||
bool mSaidGreeting;
|
bool mSaidGreeting;
|
||||||
|
int mGreetDistanceMultiplier;
|
||||||
|
float mGreetDistanceReset;
|
||||||
|
float mChance;
|
||||||
|
|
||||||
// Cached current cell location
|
// Cached current cell location
|
||||||
int mCellX;
|
int mCellX;
|
||||||
|
@ -69,6 +72,8 @@ namespace MWMechanics
|
||||||
ObstacleCheck mObstacleCheck;
|
ObstacleCheck mObstacleCheck;
|
||||||
float mDoorCheckDuration;
|
float mDoorCheckDuration;
|
||||||
int mStuckCount;
|
int mStuckCount;
|
||||||
|
|
||||||
|
float mReaction; // update some actions infrequently
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue