diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f293cb2..dea84b56b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures Bug #5370: Opening an unlocked but trapped door uses the key + Bug #5397: NPC greeting does not reset if you leave + reenter area Bug #5400: Editor: Verifier checks race of non-skin bodyparts Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index cca789a40..3bde83e94 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -7,6 +7,9 @@ #include #include +#include "../mwmechanics/actorutil.hpp" +// For MWMechanics::GreetingState + #include "../mwworld/ptr.hpp" namespace osg @@ -272,6 +275,11 @@ namespace MWBase virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0; virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; + + virtual int getGreetingTimer(const MWWorld::Ptr& ptr) const = 0; + virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0; + virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0; + virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0; }; } diff --git a/apps/openmw/mwmechanics/actor.cpp b/apps/openmw/mwmechanics/actor.cpp index f7c6b7f1c..a5c55633a 100644 --- a/apps/openmw/mwmechanics/actor.cpp +++ b/apps/openmw/mwmechanics/actor.cpp @@ -18,4 +18,44 @@ namespace MWMechanics { return mCharacterController.get(); } + + int Actor::getGreetingTimer() const + { + return mGreetingTimer; + } + + void Actor::setGreetingTimer(int timer) + { + mGreetingTimer = timer; + } + + float Actor::getAngleToPlayer() const + { + return mTargetAngleRadians; + } + + void Actor::setAngleToPlayer(float angle) + { + mTargetAngleRadians = angle; + } + + GreetingState Actor::getGreetingState() const + { + return mGreetingState; + } + + void Actor::setGreetingState(GreetingState state) + { + mGreetingState = state; + } + + bool Actor::isTurningToPlayer() const + { + return mIsTurningToPlayer; + } + + void Actor::setTurningToPlayer(bool turning) + { + mIsTurningToPlayer = turning; + } } diff --git a/apps/openmw/mwmechanics/actor.hpp b/apps/openmw/mwmechanics/actor.hpp index 119527b64..287ca420f 100644 --- a/apps/openmw/mwmechanics/actor.hpp +++ b/apps/openmw/mwmechanics/actor.hpp @@ -3,6 +3,8 @@ #include +#include "../mwmechanics/actorutil.hpp" + namespace MWRender { class Animation; @@ -27,8 +29,24 @@ namespace MWMechanics CharacterController* getCharacterController(); + 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); + private: std::unique_ptr mCharacterController; + int mGreetingTimer{0}; + float mTargetAngleRadians{0.f}; + GreetingState mGreetingState{Greet_None}; + bool mIsTurningToPlayer{false}; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 223d9fc34..adc13efa7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -447,7 +447,7 @@ namespace MWMechanics actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor; } - void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly) + void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor* actorState, bool turnOnly) { if (!actor.getClass().isActor() || actor == getPlayer()) return; @@ -460,9 +460,9 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->isSwimming(actor) || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) { - stats.setTurningToPlayer(false); - stats.setGreetingTimer(0); - stats.setGreetingState(Greet_None); + actorState->setTurningToPlayer(false); + actorState->setGreetingTimer(0); + actorState->setGreetingState(Greet_None); return; } @@ -471,14 +471,14 @@ namespace MWMechanics osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); osg::Vec3f dir = playerPos - actorPos; - if (stats.isTurningToPlayer()) + if (actorState->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))) + if (zTurn(actor, actorState->getAngleToPlayer(), osg::DegreesToRadians(5.f))) { - stats.setTurningToPlayer(false); + actorState->setTurningToPlayer(false); // An original engine launches an endless idle2 when an actor greets player. playAnimationGroup (actor, "idle2", 0, std::numeric_limits::max(), false); } @@ -493,8 +493,8 @@ namespace MWMechanics float helloDistance = static_cast(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier); - int greetingTimer = stats.getGreetingTimer(); - GreetingState greetingState = stats.getGreetingState(); + int greetingTimer = actorState->getGreetingTimer(); + GreetingState greetingState = actorState->getGreetingState(); if (greetingState == Greet_None) { if ((playerPos - actorPos).length2() <= helloDistance*helloDistance && @@ -516,7 +516,7 @@ namespace MWMechanics greetingTimer++; if (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor)) - turnActorToFacePlayer(actor, dir); + turnActorToFacePlayer(actor, actorState, dir); if (greetingTimer >= GREETING_COOLDOWN) { @@ -532,20 +532,19 @@ namespace MWMechanics greetingState = Greet_None; } - stats.setGreetingTimer(greetingTimer); - stats.setGreetingState(greetingState); + actorState->setGreetingTimer(greetingTimer); + actorState->setGreetingState(greetingState); } - void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir) + void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor* actorState, 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()) + if (!actorState->isTurningToPlayer()) { - stats.setAngleToPlayer(std::atan2(dir.x(), dir.y())); - stats.setTurningToPlayer(true); + actorState->setAngleToPlayer(std::atan2(dir.x(), dir.y())); + actorState->setTurningToPlayer(true); } } @@ -1695,7 +1694,7 @@ namespace MWMechanics if (isConscious(iter->first)) { stats.getAiSequence().execute(iter->first, *ctrl, duration); - updateGreetingState(iter->first, timerUpdateHello > 0); + updateGreetingState(iter->first, iter->second, timerUpdateHello > 0); playIdleDialogue(iter->first); updateMovementSpeed(iter->first); } @@ -2390,6 +2389,42 @@ namespace MWMechanics return ctrl->isAttackingOrSpell(); } + int Actors::getGreetingTimer(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return 0; + + return it->second->getGreetingTimer(); + } + + float Actors::getAngleToPlayer(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return 0.f; + + return it->second->getAngleToPlayer(); + } + + GreetingState Actors::getGreetingState(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return Greet_None; + + return it->second->getGreetingState(); + } + + bool Actors::isTurningToPlayer(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return false; + + return it->second->isTurningToPlayer(); + } + void Actors::fastForwardAi() { if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 4e952d1c8..25716d392 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -7,6 +7,8 @@ #include #include +#include "../mwmechanics/actorutil.hpp" + namespace ESM { class ESMReader; @@ -123,8 +125,8 @@ namespace MWMechanics void playIdleDialogue(const MWWorld::Ptr& actor); void updateMovementSpeed(const MWWorld::Ptr& actor); - void updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly); - void turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir); + void updateGreetingState(const MWWorld::Ptr& actor, Actor* actorState, bool turnOnly); + void turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor* actorState, const osg::Vec3f& dir); void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance); @@ -195,6 +197,11 @@ namespace MWMechanics bool isReadyToBlock(const MWWorld::Ptr& ptr) const; bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const; + int getGreetingTimer(const MWWorld::Ptr& ptr) const; + float getAngleToPlayer(const MWWorld::Ptr& ptr) const; + GreetingState getGreetingState(const MWWorld::Ptr& ptr) const; + bool isTurningToPlayer(const MWWorld::Ptr& ptr) const; + private: void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl); diff --git a/apps/openmw/mwmechanics/actorutil.hpp b/apps/openmw/mwmechanics/actorutil.hpp index 82a904799..cf3d92558 100644 --- a/apps/openmw/mwmechanics/actorutil.hpp +++ b/apps/openmw/mwmechanics/actorutil.hpp @@ -8,6 +8,13 @@ namespace MWWorld namespace MWMechanics { + enum GreetingState + { + Greet_None, + Greet_InProgress, + Greet_Done + }; + MWWorld::Ptr getPlayer(); bool isPlayerInCombat(); bool canActorMoveByZAxis(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index dba70316b..822523c76 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -3,6 +3,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" @@ -43,14 +44,15 @@ namespace MWMechanics bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { - auto& stats = actor.getClass().getCreatureStats(actor); + MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager(); - if (stats.isTurningToPlayer() || stats.getGreetingState() == Greet_InProgress) + if (mechMgr->isTurningToPlayer(actor) || mechMgr->getGreetingState(actor) == Greet_InProgress) return false; const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); const osg::Vec3f targetPos(mX, mY, mZ); + auto& stats = actor.getClass().getCreatureStats(actor); stats.setMovementFlag(CreatureStats::Flag_Run, false); stats.setDrawState(DrawState_Nothing); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 597453409..2c40c1ba5 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -206,7 +206,7 @@ namespace MWMechanics storage.setState(AiWanderStorage::Wander_Walking); } - GreetingState greetingState = cStats.getGreetingState(); + GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor); if (greetingState == Greet_InProgress) { if (storage.mState == AiWanderStorage::Wander_Walking) @@ -442,7 +442,7 @@ namespace MWMechanics } // Check if idle animation finished - GreetingState greetingState = actor.getClass().getCreatureStats(actor).getGreetingState(); + GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor); if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None)) { if (mPathFinder.isPathConstructed()) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 1c377540a..800b5c22f 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -23,53 +23,12 @@ 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(), mGreetingState(Greet_None), - mGreetingTimer(0), mTargetAngleRadians(0), mIsTurningToPlayer(false), mLevel (0) + mDeathAnimation(-1), mTimeOfDeath(), 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; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 7c4a83db1..a4ecb23b3 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -19,13 +19,6 @@ namespace ESM namespace MWMechanics { - enum GreetingState - { - Greet_None, - Greet_InProgress, - Greet_Done - }; - /// \brief Common creature stats /// /// @@ -77,11 +70,6 @@ namespace MWMechanics MWWorld::TimeStamp mTimeOfDeath; - GreetingState mGreetingState; - int mGreetingTimer; - float mTargetAngleRadians; - bool mIsTurningToPlayer; - public: typedef std::pair SummonKey; // private: @@ -97,18 +85,6 @@ 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); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5ca7b3cdd..88045ec44 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1951,4 +1951,24 @@ namespace MWMechanics stats.setAttribute(frameNumber, "Mechanics Actors", mActors.size()); stats.setAttribute(frameNumber, "Mechanics Objects", mObjects.size()); } + + int MechanicsManager::getGreetingTimer(const MWWorld::Ptr &ptr) const + { + return mActors.getGreetingTimer(ptr); + } + + float MechanicsManager::getAngleToPlayer(const MWWorld::Ptr &ptr) const + { + return mActors.getAngleToPlayer(ptr); + } + + GreetingState MechanicsManager::getGreetingState(const MWWorld::Ptr &ptr) const + { + return mActors.getGreetingState(ptr); + } + + bool MechanicsManager::isTurningToPlayer(const MWWorld::Ptr &ptr) const + { + return mActors.isTurningToPlayer(ptr); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 83d1b236f..6785f979f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -242,6 +242,11 @@ namespace MWMechanics virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; + virtual int getGreetingTimer(const MWWorld::Ptr& ptr) const override; + virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const override; + virtual GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override; + virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override; + private: bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set &playerFollowers); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 79639197d..603ed8836 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -430,13 +430,13 @@ namespace MWScript if (!targetPtr.isEmpty() && targetPtr.getCellRef().getRefId() == testedTargetId) targetsAreEqual = true; } - else + else if (testedTargetId == "player") // Currently the player ID is hardcoded { - bool turningToPlayer = creatureStats.isTurningToPlayer(); - bool greeting = creatureStats.getGreetingState() == MWMechanics::Greet_InProgress; + MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager(); + bool greeting = mechMgr->getGreetingState(actor) == MWMechanics::Greet_InProgress; bool sayActive = MWBase::Environment::get().getSoundManager()->sayActive(actor); - if (turningToPlayer || (greeting && sayActive)) - targetsAreEqual = (testedTargetId == "player"); // Currently the player ID is hardcoded + if ((greeting && sayActive) || mechMgr->isTurningToPlayer(actor)) + targetsAreEqual = true; } runtime.push(int(targetsAreEqual)); }