mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 06:53:53 +00:00
Split greetings from AiWander (bug #4594)
This commit is contained in:
parent
b7a1e6561b
commit
69aceb5c1e
8 changed files with 204 additions and 109 deletions
|
@ -31,6 +31,7 @@
|
||||||
Bug #4411: Reloading a saved game while falling prevents damage in some cases
|
Bug #4411: Reloading a saved game while falling prevents damage in some cases
|
||||||
Bug #4456: AiActivate should not be cancelled after target activation
|
Bug #4456: AiActivate should not be cancelled after target activation
|
||||||
Bug #4540: Rain delay when exiting water
|
Bug #4540: Rain delay when exiting water
|
||||||
|
Bug #4594: Actors without AI packages don't use Hello dialogue
|
||||||
Bug #4600: Crash when no sound output is available or --no-sound is used.
|
Bug #4600: Crash when no sound output is available or --no-sound is used.
|
||||||
Bug #4639: Black screen after completing first mages guild mission + training
|
Bug #4639: Black screen after completing first mages guild mission + training
|
||||||
Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog
|
Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "../mwrender/vismask.hpp"
|
#include "../mwrender/vismask.hpp"
|
||||||
|
|
||||||
#include "spellcasting.hpp"
|
#include "spellcasting.hpp"
|
||||||
|
#include "steering.hpp"
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
#include "creaturestats.hpp"
|
#include "creaturestats.hpp"
|
||||||
#include "movement.hpp"
|
#include "movement.hpp"
|
||||||
|
@ -141,6 +142,9 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
static const int GREETING_SHOULD_START = 4; //how many updates should pass before NPC can greet player
|
||||||
|
static const int GREETING_SHOULD_END = 10;
|
||||||
|
|
||||||
class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor
|
class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -397,6 +401,113 @@ namespace MWMechanics
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly)
|
||||||
|
{
|
||||||
|
if (!actor.getClass().isActor() || actor == getPlayer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||||
|
int hello = stats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||||
|
if (hello == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getWorld()->isSwimming(actor))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MWWorld::Ptr player = getPlayer();
|
||||||
|
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
||||||
|
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||||
|
osg::Vec3f dir = playerPos - actorPos;
|
||||||
|
|
||||||
|
const MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||||
|
int packageId = seq.getTypeId();
|
||||||
|
|
||||||
|
if (seq.isInCombat() || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
|
||||||
|
{
|
||||||
|
stats.setTurningToPlayer(false);
|
||||||
|
stats.setGreetingTimer(0);
|
||||||
|
stats.setGreetingState(Greet_None);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.isTurningToPlayer())
|
||||||
|
{
|
||||||
|
// Reduce the turning animation glitch by using a *HUGE* value of
|
||||||
|
// epsilon... TODO: a proper fix might be in either the physics or the
|
||||||
|
// animation subsystem
|
||||||
|
if (zTurn(actor, stats.getAngleToPlayer(), osg::DegreesToRadians(5.f)))
|
||||||
|
{
|
||||||
|
stats.setTurningToPlayer(false);
|
||||||
|
// An original engine launches an endless idle2 when an actor greets player.
|
||||||
|
playAnimationGroup (actor, "idle2", 0, std::numeric_limits<int>::max(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnOnly)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Play a random voice greeting if the player gets too close
|
||||||
|
float helloDistance = static_cast<float>(hello);
|
||||||
|
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
||||||
|
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
|
||||||
|
|
||||||
|
helloDistance *= iGreetDistanceMultiplier;
|
||||||
|
|
||||||
|
int greetingTimer = stats.getGreetingTimer();
|
||||||
|
GreetingState greetingState = stats.getGreetingState();
|
||||||
|
if (greetingState == Greet_None)
|
||||||
|
{
|
||||||
|
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
|
||||||
|
!player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed()
|
||||||
|
&& MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
||||||
|
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
||||||
|
greetingTimer++;
|
||||||
|
|
||||||
|
if (greetingTimer >= GREETING_SHOULD_START)
|
||||||
|
{
|
||||||
|
greetingState = Greet_InProgress;
|
||||||
|
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
||||||
|
greetingTimer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (greetingState == Greet_InProgress)
|
||||||
|
{
|
||||||
|
greetingTimer++;
|
||||||
|
|
||||||
|
turnActorToFacePlayer(actor, dir);
|
||||||
|
|
||||||
|
if (greetingTimer >= GREETING_SHOULD_END)
|
||||||
|
{
|
||||||
|
greetingState = Greet_Done;
|
||||||
|
greetingTimer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (greetingState == Greet_Done)
|
||||||
|
{
|
||||||
|
float resetDist = 2 * helloDistance;
|
||||||
|
if ((playerPos - actorPos).length2() >= resetDist*resetDist)
|
||||||
|
greetingState = Greet_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.setGreetingTimer(greetingTimer);
|
||||||
|
stats.setGreetingState(greetingState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir)
|
||||||
|
{
|
||||||
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
|
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||||
|
|
||||||
|
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||||
|
if (!stats.isTurningToPlayer())
|
||||||
|
{
|
||||||
|
stats.setAngleToPlayer(std::atan2(dir.x(), dir.y()));
|
||||||
|
stats.setTurningToPlayer(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer)
|
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer)
|
||||||
{
|
{
|
||||||
// No combat for totally static creatures
|
// No combat for totally static creatures
|
||||||
|
@ -1409,11 +1520,13 @@ namespace MWMechanics
|
||||||
static float timerUpdateAITargets = 0;
|
static float timerUpdateAITargets = 0;
|
||||||
static float timerUpdateHeadTrack = 0;
|
static float timerUpdateHeadTrack = 0;
|
||||||
static float timerUpdateEquippedLight = 0;
|
static float timerUpdateEquippedLight = 0;
|
||||||
|
static float timerUpdateHello = 0;
|
||||||
const float updateEquippedLightInterval = 1.0f;
|
const float updateEquippedLightInterval = 1.0f;
|
||||||
|
|
||||||
// target lists get updated once every 1.0 sec
|
// target lists get updated once every 1.0 sec
|
||||||
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
|
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
|
||||||
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
|
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
|
||||||
|
if (timerUpdateHello >= 0.25f) timerUpdateHello = 0;
|
||||||
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
|
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
|
||||||
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
|
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
|
||||||
|
|
||||||
|
@ -1532,6 +1645,7 @@ namespace MWMechanics
|
||||||
if (isConscious(iter->first))
|
if (isConscious(iter->first))
|
||||||
{
|
{
|
||||||
stats.getAiSequence().execute(iter->first, *ctrl, duration);
|
stats.getAiSequence().execute(iter->first, *ctrl, duration);
|
||||||
|
updateGreetingState(iter->first, timerUpdateHello > 0);
|
||||||
playIdleDialogue(iter->first);
|
playIdleDialogue(iter->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1554,6 +1668,7 @@ namespace MWMechanics
|
||||||
timerUpdateAITargets += duration;
|
timerUpdateAITargets += duration;
|
||||||
timerUpdateHeadTrack += duration;
|
timerUpdateHeadTrack += duration;
|
||||||
timerUpdateEquippedLight += duration;
|
timerUpdateEquippedLight += duration;
|
||||||
|
timerUpdateHello += duration;
|
||||||
mTimerDisposeSummonsCorpses += duration;
|
mTimerDisposeSummonsCorpses += duration;
|
||||||
|
|
||||||
// Animation/movement update
|
// Animation/movement update
|
||||||
|
|
|
@ -121,6 +121,8 @@ namespace MWMechanics
|
||||||
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer);
|
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer);
|
||||||
|
|
||||||
void playIdleDialogue(const MWWorld::Ptr& actor);
|
void playIdleDialogue(const MWWorld::Ptr& actor);
|
||||||
|
void updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly);
|
||||||
|
void turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir);
|
||||||
|
|
||||||
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
|
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
|
||||||
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
|
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
|
||||||
|
|
|
@ -43,11 +43,16 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||||
{
|
{
|
||||||
|
auto& stats = actor.getClass().getCreatureStats(actor);
|
||||||
|
|
||||||
|
if (stats.isTurningToPlayer() || stats.getGreetingState() == Greet_InProgress)
|
||||||
|
return false;
|
||||||
|
|
||||||
const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||||
const osg::Vec3f targetPos(mX, mY, mZ);
|
const osg::Vec3f targetPos(mX, mY, mZ);
|
||||||
|
|
||||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
|
stats.setMovementFlag(CreatureStats::Flag_Run, false);
|
||||||
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
stats.setDrawState(DrawState_Nothing);
|
||||||
|
|
||||||
if (!isWithinMaxRange(targetPos, actorPos))
|
if (!isWithinMaxRange(targetPos, actorPos))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
#include "pathgrid.hpp"
|
#include "pathgrid.hpp"
|
||||||
#include "creaturestats.hpp"
|
#include "creaturestats.hpp"
|
||||||
#include "steering.hpp"
|
|
||||||
#include "movement.hpp"
|
#include "movement.hpp"
|
||||||
#include "coordinateconverter.hpp"
|
#include "coordinateconverter.hpp"
|
||||||
#include "actorutil.hpp"
|
#include "actorutil.hpp"
|
||||||
|
@ -26,8 +25,6 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
static const int COUNT_BEFORE_RESET = 10;
|
static const int COUNT_BEFORE_RESET = 10;
|
||||||
static const float DOOR_CHECK_INTERVAL = 1.5f;
|
static const float DOOR_CHECK_INTERVAL = 1.5f;
|
||||||
static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player
|
|
||||||
static const int GREETING_SHOULD_END = 10;
|
|
||||||
|
|
||||||
// to prevent overcrowding
|
// to prevent overcrowding
|
||||||
static const int DESTINATION_TOLERANCE = 64;
|
static const int DESTINATION_TOLERANCE = 64;
|
||||||
|
@ -176,6 +173,17 @@ namespace MWMechanics
|
||||||
storage.setState(AiWanderStorage::Wander_Walking);
|
storage.setState(AiWanderStorage::Wander_Walking);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GreetingState greetingState = cStats.getGreetingState();
|
||||||
|
if (greetingState == Greet_InProgress)
|
||||||
|
{
|
||||||
|
if (storage.mState == AiWanderStorage::Wander_Walking)
|
||||||
|
{
|
||||||
|
stopWalking(actor, storage, false);
|
||||||
|
mObstacleCheck.clear();
|
||||||
|
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
doPerFrameActionsForState(actor, duration, storage);
|
doPerFrameActionsForState(actor, duration, storage);
|
||||||
|
|
||||||
float& lastReaction = storage.mReaction;
|
float& lastReaction = storage.mReaction;
|
||||||
|
@ -245,13 +253,7 @@ namespace MWMechanics
|
||||||
if(mDistance && cellChange)
|
if(mDistance && cellChange)
|
||||||
mDistance = 0;
|
mDistance = 0;
|
||||||
|
|
||||||
// Allow interrupting a walking actor to trigger a greeting
|
|
||||||
AiWanderStorage::WanderState& wanderState = storage.mState;
|
AiWanderStorage::WanderState& wanderState = storage.mState;
|
||||||
if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking))
|
|
||||||
{
|
|
||||||
playGreetingIfPlayerGetsTooClose(actor, storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((wanderState == AiWanderStorage::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
|
||||||
|
@ -416,19 +418,9 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool& rotate = storage.mTurnActorGivingGreetingToFacePlayer;
|
|
||||||
if (rotate)
|
|
||||||
{
|
|
||||||
// Reduce the turning animation glitch by using a *HUGE* value of
|
|
||||||
// epsilon... TODO: a proper fix might be in either the physics or the
|
|
||||||
// animation subsystem
|
|
||||||
if (zTurn(actor, storage.mTargetAngleRadians, osg::DegreesToRadians(5.f)))
|
|
||||||
rotate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if idle animation finished
|
// Check if idle animation finished
|
||||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
GreetingState greetingState = actor.getClass().getCreatureStats(actor).getGreetingState();
|
||||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None))
|
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
|
||||||
{
|
{
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
storage.setState(AiWanderStorage::Wander_Walking);
|
storage.setState(AiWanderStorage::Wander_Walking);
|
||||||
|
@ -517,74 +509,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
|
||||||
{
|
|
||||||
// Play a random voice greeting if the player gets too close
|
|
||||||
int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified();
|
|
||||||
float helloDistance = static_cast<float>(hello);
|
|
||||||
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
|
||||||
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
|
|
||||||
|
|
||||||
helloDistance *= iGreetDistanceMultiplier;
|
|
||||||
|
|
||||||
MWWorld::Ptr player = getPlayer();
|
|
||||||
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
|
||||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
|
||||||
|
|
||||||
int& greetingTimer = storage.mGreetingTimer;
|
|
||||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
|
||||||
if (greetingState == AiWanderStorage::Greet_None)
|
|
||||||
{
|
|
||||||
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
|
|
||||||
!player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed()
|
|
||||||
&& MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
|
||||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
|
||||||
greetingTimer++;
|
|
||||||
|
|
||||||
if (greetingTimer >= GREETING_SHOULD_START)
|
|
||||||
{
|
|
||||||
greetingState = AiWanderStorage::Greet_InProgress;
|
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
|
||||||
greetingTimer = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (greetingState == AiWanderStorage::Greet_InProgress)
|
|
||||||
{
|
|
||||||
greetingTimer++;
|
|
||||||
|
|
||||||
if (storage.mState == AiWanderStorage::Wander_Walking)
|
|
||||||
{
|
|
||||||
stopWalking(actor, storage, false);
|
|
||||||
mObstacleCheck.clear();
|
|
||||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
|
||||||
}
|
|
||||||
|
|
||||||
turnActorToFacePlayer(actorPos, playerPos, storage);
|
|
||||||
|
|
||||||
if (greetingTimer >= GREETING_SHOULD_END)
|
|
||||||
{
|
|
||||||
greetingState = AiWanderStorage::Greet_Done;
|
|
||||||
greetingTimer = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (greetingState == AiWanderStorage::Greet_Done)
|
|
||||||
{
|
|
||||||
float resetDist = 2 * helloDistance;
|
|
||||||
if ((playerPos - actorPos).length2() >= resetDist*resetDist)
|
|
||||||
greetingState = AiWanderStorage::Greet_None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AiWander::turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage)
|
|
||||||
{
|
|
||||||
osg::Vec3f dir = playerPosition - actorPosition;
|
|
||||||
|
|
||||||
float faceAngleRadians = std::atan2(dir.x(), dir.y());
|
|
||||||
storage.mTargetAngleRadians = faceAngleRadians;
|
|
||||||
storage.mTurnActorGivingGreetingToFacePlayer = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
|
void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,21 +25,8 @@ namespace MWMechanics
|
||||||
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
|
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
|
||||||
struct AiWanderStorage : AiTemporaryBase
|
struct AiWanderStorage : AiTemporaryBase
|
||||||
{
|
{
|
||||||
// the z rotation angle to reach
|
|
||||||
// when mTurnActorGivingGreetingToFacePlayer is true
|
|
||||||
float mTargetAngleRadians;
|
|
||||||
bool mTurnActorGivingGreetingToFacePlayer;
|
|
||||||
float mReaction; // update some actions infrequently
|
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
|
const MWWorld::CellStore* mCell; // for detecting cell change
|
||||||
|
|
||||||
// AiWander states
|
// AiWander states
|
||||||
|
@ -72,11 +59,7 @@ namespace MWMechanics
|
||||||
int mStuckCount;
|
int mStuckCount;
|
||||||
|
|
||||||
AiWanderStorage():
|
AiWanderStorage():
|
||||||
mTargetAngleRadians(0),
|
|
||||||
mTurnActorGivingGreetingToFacePlayer(false),
|
|
||||||
mReaction(0),
|
mReaction(0),
|
||||||
mSaidGreeting(Greet_None),
|
|
||||||
mGreetingTimer(0),
|
|
||||||
mCell(nullptr),
|
mCell(nullptr),
|
||||||
mState(Wander_ChooseAction),
|
mState(Wander_ChooseAction),
|
||||||
mIsWanderingManually(false),
|
mIsWanderingManually(false),
|
||||||
|
@ -136,7 +119,6 @@ namespace MWMechanics
|
||||||
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||||
short unsigned getRandomIdle();
|
short unsigned getRandomIdle();
|
||||||
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
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, float duration, AiWanderStorage& storage);
|
void evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||||
void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
|
void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
|
||||||
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||||
|
|
|
@ -23,12 +23,53 @@ namespace MWMechanics
|
||||||
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
||||||
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
||||||
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),
|
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),
|
||||||
mDeathAnimation(-1), mTimeOfDeath(), mLevel (0)
|
mDeathAnimation(-1), mTimeOfDeath(), mGreetingState(Greet_None),
|
||||||
|
mGreetingTimer(0), mTargetAngleRadians(0), mIsTurningToPlayer(false), mLevel (0)
|
||||||
{
|
{
|
||||||
for (int i=0; i<4; ++i)
|
for (int i=0; i<4; ++i)
|
||||||
mAiSettings[i] = 0;
|
mAiSettings[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MWMechanics::CreatureStats::getGreetingTimer() const
|
||||||
|
{
|
||||||
|
return mGreetingTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::CreatureStats::setGreetingTimer(int timer)
|
||||||
|
{
|
||||||
|
mGreetingTimer = timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MWMechanics::CreatureStats::getAngleToPlayer() const
|
||||||
|
{
|
||||||
|
return mTargetAngleRadians;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::CreatureStats::setAngleToPlayer(float angle)
|
||||||
|
{
|
||||||
|
mTargetAngleRadians = angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
GreetingState MWMechanics::CreatureStats::getGreetingState() const
|
||||||
|
{
|
||||||
|
return mGreetingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::CreatureStats::setGreetingState(GreetingState state)
|
||||||
|
{
|
||||||
|
mGreetingState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWMechanics::CreatureStats::isTurningToPlayer() const
|
||||||
|
{
|
||||||
|
return mIsTurningToPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::CreatureStats::setTurningToPlayer(bool turning)
|
||||||
|
{
|
||||||
|
mIsTurningToPlayer = turning;
|
||||||
|
}
|
||||||
|
|
||||||
const AiSequence& CreatureStats::getAiSequence() const
|
const AiSequence& CreatureStats::getAiSequence() const
|
||||||
{
|
{
|
||||||
return mAiSequence;
|
return mAiSequence;
|
||||||
|
|
|
@ -19,6 +19,13 @@ namespace ESM
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
enum GreetingState
|
||||||
|
{
|
||||||
|
Greet_None,
|
||||||
|
Greet_InProgress,
|
||||||
|
Greet_Done
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Common creature stats
|
/// \brief Common creature stats
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
@ -70,6 +77,11 @@ namespace MWMechanics
|
||||||
|
|
||||||
MWWorld::TimeStamp mTimeOfDeath;
|
MWWorld::TimeStamp mTimeOfDeath;
|
||||||
|
|
||||||
|
GreetingState mGreetingState;
|
||||||
|
int mGreetingTimer;
|
||||||
|
float mTargetAngleRadians;
|
||||||
|
bool mIsTurningToPlayer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
|
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
|
||||||
private:
|
private:
|
||||||
|
@ -85,6 +97,18 @@ namespace MWMechanics
|
||||||
public:
|
public:
|
||||||
CreatureStats();
|
CreatureStats();
|
||||||
|
|
||||||
|
int getGreetingTimer() const;
|
||||||
|
void setGreetingTimer(int timer);
|
||||||
|
|
||||||
|
float getAngleToPlayer() const;
|
||||||
|
void setAngleToPlayer(float angle);
|
||||||
|
|
||||||
|
GreetingState getGreetingState() const;
|
||||||
|
void setGreetingState(GreetingState state);
|
||||||
|
|
||||||
|
bool isTurningToPlayer() const;
|
||||||
|
void setTurningToPlayer(bool turning);
|
||||||
|
|
||||||
DrawState_ getDrawState() const;
|
DrawState_ getDrawState() const;
|
||||||
void setDrawState(DrawState_ state);
|
void setDrawState(DrawState_ state);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue