From 05d5d7d57b97191f6c01ebcbb079b900d63e201c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 13 Jan 2019 05:11:51 +0300 Subject: [PATCH] Account for stances when the player is in air (bug #4797) Make GetPCRunning and GetPCSneaking return 1 if the player is in air while the stance is toggled Make sneaking skill usage possible if sneaking stance is toggled while in air --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 147 ++++++++++++--------- apps/openmw/mwmechanics/actors.hpp | 4 + apps/openmw/mwscript/controlextensions.cpp | 21 +-- 4 files changed, 99 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa5a91a9d..36eb5bed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Bug #4768: Fallback numerical value recovery chokes on invalid arguments Bug #4775: Slowfall effect resets player jumping flag Bug #4778: Interiors of Illusion puzzle in Sotha Sil Expanded mod is broken + Bug #4797: Player sneaking and running stances are not accounted for when in air Bug #4800: Standing collisions are not updated immediately when an object is teleported without a cell change Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4166ece11..b91fb1d42 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1513,72 +1513,7 @@ namespace MWMechanics } killDeadActors(); - - static float sneakTimer = 0.f; // times update of sneak icon - - // if player is in sneak state see if anyone detects him - if (playerCharacter && playerCharacter->isSneaking()) - { - static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice" - - const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - const int radius = esmStore.get().find("fSneakUseDist")->mValue.getInteger(); - - static float fSneakUseDelay = esmStore.get().find("fSneakUseDelay")->mValue.getFloat(); - - if (sneakTimer >= fSneakUseDelay) - sneakTimer = 0.f; - - if (sneakTimer == 0.f) - { - // Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress. - bool avoidedNotice = false; - - bool detected = false; - - for (PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) - { - MWWorld::Ptr observer = iter->first; - - if (iter->first == player) // not the player - continue; - - if (observer.getClass().getCreatureStats(observer).isDead()) - continue; - - // is the player in range and can they be detected - if ((observer.getRefData().getPosition().asVec3() - playerPos).length2() <= radius*radius - && MWBase::Environment::get().getWorld()->getLOS(player, observer)) - { - if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, observer)) - { - detected = true; - avoidedNotice = false; - MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); - break; - } - else - avoidedNotice = true; - } - } - - if (sneakSkillTimer >= fSneakUseDelay) - sneakSkillTimer = 0.f; - - if (avoidedNotice && sneakSkillTimer == 0.f) - player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 0); - - if (!detected) - MWBase::Environment::get().getWindowManager()->setSneakVisibility(true); - } - sneakTimer += duration; - sneakSkillTimer += duration; - } - else - { - sneakTimer = 0.f; - MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); - } + updateSneaking(playerCharacter, duration); } updateCombatMusic(); @@ -1727,6 +1662,86 @@ namespace MWMechanics fastForwardAi(); } + void Actors::updateSneaking(CharacterController* ctrl, float duration) + { + static float sneakTimer = 0.f; // Times update of sneak icon + + if (!ctrl) + { + MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); + return; + } + + MWWorld::Ptr player = getPlayer(); + + CreatureStats& stats = player.getClass().getCreatureStats(player); + MWBase::World* world = MWBase::Environment::get().getWorld(); + + bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); + bool inair = !world->isOnGround(player) && !world->isSwimming(player) && !world->isFlying(player); + sneaking = sneaking && (ctrl->isSneaking() || inair); + + if (!sneaking) + { + MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); + return; + } + + static float sneakSkillTimer = 0.f; // Times sneak skill progress from "avoid notice" + + const MWWorld::Store& gmst = world->getStore().get(); + static const float fSneakUseDist = gmst.find("fSneakUseDist")->mValue.getFloat(); + static const float fSneakUseDelay = gmst.find("fSneakUseDelay")->mValue.getFloat(); + + if (sneakTimer >= fSneakUseDelay) + sneakTimer = 0.f; + + if (sneakTimer == 0.f) + { + // Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress. + bool avoidedNotice = false; + bool detected = false; + + std::vector observers; + osg::Vec3f position(player.getRefData().getPosition().asVec3()); + float radius = std::min(fSneakUseDist, mActorsProcessingRange); + getObjectsInRange(position, radius, observers); + + for (const MWWorld::Ptr &observer : observers) + { + if (observer == player || observer.getClass().getCreatureStats(observer).isDead()) + continue; + + if (world->getLOS(player, observer)) + { + if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, observer)) + { + detected = true; + avoidedNotice = false; + MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); + break; + } + else + { + avoidedNotice = true; + } + } + } + + if (sneakSkillTimer >= fSneakUseDelay) + sneakSkillTimer = 0.f; + + if (avoidedNotice && sneakSkillTimer == 0.f) + player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 0); + + if (!detected) + MWBase::Environment::get().getWindowManager()->setSneakVisibility(true); + } + + sneakTimer += duration; + sneakSkillTimer += duration; + } + int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const { float healthPerHour, magickaPerHour; diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 61879c432..75f1b394e 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -19,6 +19,7 @@ namespace MWWorld namespace MWMechanics { class Actor; + class CharacterController; class CreatureStats; class Actors @@ -108,6 +109,9 @@ namespace MWMechanics void rest(bool sleep); ///< Update actors while the player is waiting or sleeping. This should be called every hour. + void updateSneaking(CharacterController* ctrl, float duration); + ///< Update the sneaking indicator state according to the given player character controller. + void restoreDynamicStats(const MWWorld::Ptr& actor, bool sleep); int getHoursToRest(const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 21b3b5587..0d5b1bf3b 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -16,7 +16,6 @@ #include "../mwworld/ptr.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/movement.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -168,12 +167,15 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - const MWWorld::Class &cls = ptr.getClass(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + MWBase::World* world = MWBase::Environment::get().getWorld(); - bool isRunning = MWBase::Environment::get().getMechanicsManager()->isRunning(ptr); + bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Run); + bool running = MWBase::Environment::get().getMechanicsManager()->isRunning(ptr); + bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr); - runtime.push (isRunning && cls.getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)); + runtime.push(stanceOn && (running || inair)); } }; @@ -184,11 +186,14 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); - const MWWorld::Class &cls = ptr.getClass(); + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + MWBase::World* world = MWBase::Environment::get().getWorld(); - bool isSneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr); + bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); + bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr); + bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr); - runtime.push (isSneaking && cls.getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak)); + runtime.push(stanceOn && (sneaking || inair)); } };