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
pull/541/head
Capostrophic 6 years ago
parent c01672a364
commit 05d5d7d57b

@ -18,6 +18,7 @@
Bug #4768: Fallback numerical value recovery chokes on invalid arguments Bug #4768: Fallback numerical value recovery chokes on invalid arguments
Bug #4775: Slowfall effect resets player jumping flag Bug #4775: Slowfall effect resets player jumping flag
Bug #4778: Interiors of Illusion puzzle in Sotha Sil Expanded mod is broken 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 Bug #4800: Standing collisions are not updated immediately when an object is teleported without a cell change
Feature #2229: Improve pathfinding AI Feature #2229: Improve pathfinding AI
Feature #3442: Default values for fallbacks from ini file Feature #3442: Default values for fallbacks from ini file

@ -1513,72 +1513,7 @@ namespace MWMechanics
} }
killDeadActors(); killDeadActors();
updateSneaking(playerCharacter, duration);
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<ESM::GameSetting>().find("fSneakUseDist")->mValue.getInteger();
static float fSneakUseDelay = esmStore.get<ESM::GameSetting>().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);
}
} }
updateCombatMusic(); updateCombatMusic();
@ -1727,6 +1662,86 @@ namespace MWMechanics
fastForwardAi(); 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<ESM::GameSetting>& gmst = world->getStore().get<ESM::GameSetting>();
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<MWWorld::Ptr> 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 int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const
{ {
float healthPerHour, magickaPerHour; float healthPerHour, magickaPerHour;

@ -19,6 +19,7 @@ namespace MWWorld
namespace MWMechanics namespace MWMechanics
{ {
class Actor; class Actor;
class CharacterController;
class CreatureStats; class CreatureStats;
class Actors class Actors
@ -108,6 +109,9 @@ namespace MWMechanics
void rest(bool sleep); void rest(bool sleep);
///< Update actors while the player is waiting or sleeping. This should be called every hour. ///< 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); void restoreDynamicStats(const MWWorld::Ptr& actor, bool sleep);
int getHoursToRest(const MWWorld::Ptr& ptr) const; int getHoursToRest(const MWWorld::Ptr& ptr) const;

@ -16,7 +16,6 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/movement.hpp"
#include "interpretercontext.hpp" #include "interpretercontext.hpp"
#include "ref.hpp" #include "ref.hpp"
@ -169,11 +168,14 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); 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 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) virtual void execute (Interpreter::Runtime& runtime)
{ {
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); 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));
} }
}; };

Loading…
Cancel
Save