mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 23:26:43 +00:00 
			
		
		
		
	Merge pull request #2552 from akortunov/greeting
Split greetings from AiWander
This commit is contained in:
		
						commit
						ccb557edf0
					
				
					 8 changed files with 204 additions and 109 deletions
				
			
		|  | @ -32,6 +32,7 @@ | |||
|     Bug #4449: Value returned by GetWindSpeed is incorrect | ||||
|     Bug #4456: AiActivate should not be cancelled after target activation | ||||
|     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 #4639: Black screen after completing first mages guild mission + training | ||||
|     Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ | |||
| #include "../mwrender/vismask.hpp" | ||||
| 
 | ||||
| #include "spellcasting.hpp" | ||||
| #include "steering.hpp" | ||||
| #include "npcstats.hpp" | ||||
| #include "creaturestats.hpp" | ||||
| #include "movement.hpp" | ||||
|  | @ -141,6 +142,9 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float | |||
| 
 | ||||
| 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 | ||||
|     { | ||||
|     public: | ||||
|  | @ -397,6 +401,113 @@ namespace MWMechanics | |||
|             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) | ||||
|     { | ||||
|         // No combat for totally static creatures
 | ||||
|  | @ -1409,11 +1520,13 @@ namespace MWMechanics | |||
|             static float timerUpdateAITargets = 0; | ||||
|             static float timerUpdateHeadTrack = 0; | ||||
|             static float timerUpdateEquippedLight = 0; | ||||
|             static float timerUpdateHello = 0; | ||||
|             const float updateEquippedLightInterval = 1.0f; | ||||
| 
 | ||||
|             // target lists get updated once every 1.0 sec
 | ||||
|             if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0; | ||||
|             if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0; | ||||
|             if (timerUpdateHello >= 0.25f) timerUpdateHello = 0; | ||||
|             if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0; | ||||
|             if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0; | ||||
| 
 | ||||
|  | @ -1532,6 +1645,7 @@ namespace MWMechanics | |||
|                             if (isConscious(iter->first)) | ||||
|                             { | ||||
|                                 stats.getAiSequence().execute(iter->first, *ctrl, duration); | ||||
|                                 updateGreetingState(iter->first, timerUpdateHello > 0); | ||||
|                                 playIdleDialogue(iter->first); | ||||
|                             } | ||||
|                         } | ||||
|  | @ -1554,6 +1668,7 @@ namespace MWMechanics | |||
|             timerUpdateAITargets += duration; | ||||
|             timerUpdateHeadTrack += duration; | ||||
|             timerUpdateEquippedLight += duration; | ||||
|             timerUpdateHello += duration; | ||||
|             mTimerDisposeSummonsCorpses += duration; | ||||
| 
 | ||||
|             // 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 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, | ||||
|                                             MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance); | ||||
|  |  | |||
|  | @ -43,11 +43,16 @@ namespace MWMechanics | |||
| 
 | ||||
|     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 targetPos(mX, mY, mZ); | ||||
| 
 | ||||
|         actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); | ||||
|         actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); | ||||
|         stats.setMovementFlag(CreatureStats::Flag_Run, false); | ||||
|         stats.setDrawState(DrawState_Nothing); | ||||
| 
 | ||||
|         if (!isWithinMaxRange(targetPos, actorPos)) | ||||
|             return false; | ||||
|  |  | |||
|  | @ -17,7 +17,6 @@ | |||
| 
 | ||||
| #include "pathgrid.hpp" | ||||
| #include "creaturestats.hpp" | ||||
| #include "steering.hpp" | ||||
| #include "movement.hpp" | ||||
| #include "coordinateconverter.hpp" | ||||
| #include "actorutil.hpp" | ||||
|  | @ -26,8 +25,6 @@ namespace MWMechanics | |||
| { | ||||
|     static const int COUNT_BEFORE_RESET = 10; | ||||
|     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
 | ||||
|     static const int DESTINATION_TOLERANCE = 64; | ||||
|  | @ -176,6 +173,17 @@ namespace MWMechanics | |||
|                 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); | ||||
| 
 | ||||
|         float& lastReaction = storage.mReaction; | ||||
|  | @ -245,13 +253,7 @@ namespace MWMechanics | |||
|         if(mDistance && cellChange) | ||||
|             mDistance = 0; | ||||
| 
 | ||||
|         // Allow interrupting a walking actor to trigger a greeting
 | ||||
|         AiWanderStorage::WanderState& wanderState = storage.mState; | ||||
|         if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking)) | ||||
|         { | ||||
|             playGreetingIfPlayerGetsTooClose(actor, storage); | ||||
|         } | ||||
| 
 | ||||
|         if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid) | ||||
|         { | ||||
|             // 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
 | ||||
|         AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting; | ||||
|         if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None)) | ||||
|         GreetingState greetingState = actor.getClass().getCreatureStats(actor).getGreetingState(); | ||||
|         if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None)) | ||||
|         { | ||||
|             if (mPathFinder.isPathConstructed()) | ||||
|                 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) | ||||
|     { | ||||
|  |  | |||
|  | @ -25,21 +25,8 @@ namespace MWMechanics | |||
|     /// \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
 | ||||
|  | @ -72,11 +59,7 @@ namespace MWMechanics | |||
|         int mStuckCount; | ||||
| 
 | ||||
|         AiWanderStorage(): | ||||
|             mTargetAngleRadians(0), | ||||
|             mTurnActorGivingGreetingToFacePlayer(false), | ||||
|             mReaction(0), | ||||
|             mSaidGreeting(Greet_None), | ||||
|             mGreetingTimer(0), | ||||
|             mCell(nullptr), | ||||
|             mState(Wander_ChooseAction), | ||||
|             mIsWanderingManually(false), | ||||
|  | @ -136,7 +119,6 @@ namespace MWMechanics | |||
|             bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); | ||||
|             short unsigned getRandomIdle(); | ||||
|             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 turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage); | ||||
|             void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); | ||||
|  |  | |||
|  | @ -23,12 +23,53 @@ namespace MWMechanics | |||
|           mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), | ||||
|           mHitRecovery(false), mBlock(false), mMovementFlags(0), | ||||
|           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) | ||||
|             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 | ||||
|     { | ||||
|         return mAiSequence; | ||||
|  |  | |||
|  | @ -19,6 +19,13 @@ namespace ESM | |||
| 
 | ||||
| namespace MWMechanics | ||||
| { | ||||
|     enum GreetingState | ||||
|     { | ||||
|         Greet_None, | ||||
|         Greet_InProgress, | ||||
|         Greet_Done | ||||
|     }; | ||||
| 
 | ||||
|     /// \brief Common creature stats
 | ||||
|     ///
 | ||||
|     ///
 | ||||
|  | @ -70,6 +77,11 @@ namespace MWMechanics | |||
| 
 | ||||
|         MWWorld::TimeStamp mTimeOfDeath; | ||||
| 
 | ||||
|         GreetingState mGreetingState; | ||||
|         int mGreetingTimer; | ||||
|         float mTargetAngleRadians; | ||||
|         bool mIsTurningToPlayer; | ||||
| 
 | ||||
|     public: | ||||
|         typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
 | ||||
|     private: | ||||
|  | @ -85,6 +97,18 @@ namespace MWMechanics | |||
|     public: | ||||
|         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; | ||||
|         void setDrawState(DrawState_ state); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue