diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6c9fa67032..d807a823b6 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -314,6 +314,7 @@ namespace MWBase virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; + virtual bool isGravity(const MWWorld::Ptr &ptr) const = 0; virtual void togglePOV() = 0; virtual void togglePreviewMode(bool enable) = 0; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ea49ae4a4c..a90b859558 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -470,6 +470,34 @@ namespace MWClass return x; } + float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const + { + const float fallDistanceMin = MWBase::Environment::get().getWorld()->getStore().get().find ("fFallDamageDistanceMin")->getFloat(); + + if(fallHeight>=fallDistanceMin) + { + const float acrobaticsSkill = MWWorld::Class::get(ptr).getNpcStats (ptr).getSkill (ESM::Skill::Acrobatics).getModified(); + const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const float jumpSpellBonus = npcdata->mCreatureStats.getMagicEffects().get(MWMechanics::EffectKey(9/*jump*/)).mMagnitude; + const float fallAcroBase = MWBase::Environment::get().getWorld()->getStore().get().find ("fFallAcroBase")->getFloat(); + const float fallAcroMult = MWBase::Environment::get().getWorld()->getStore().get().find ("fFallAcroMult")->getFloat(); + const float fallDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find ("fFallDistanceBase")->getFloat(); + const float fallDistanceMult = MWBase::Environment::get().getWorld()->getStore().get().find ("fFallDistanceMult")->getFloat(); + + float x = fallHeight - fallDistanceMin; + x -= (1.5 * acrobaticsSkill) + jumpSpellBonus; + x = std::max(0.0f, x); + + float a = fallAcroBase + (fallAcroMult * (100 - acrobaticsSkill)); + x = fallDistanceBase + (fallDistanceMult * x); + x *= a; + + return x; + } + + return 0; + } + MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index d1a9158fdf..47d32d88f6 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -91,6 +91,9 @@ namespace MWClass virtual float getJump(const MWWorld::Ptr &ptr) const; ///< Return jump velocity (not accounting for movement) + virtual float getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const; + ///< Return amount of health points lost when falling + virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; ///< Return desired movement. diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b82fc4dc56..f3c8629474 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -31,6 +31,9 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwmechanics/stat.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" namespace MWMechanics { @@ -104,7 +107,8 @@ static void getStateInfo(CharacterState state, std::string *group) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop) - : mPtr(ptr), mAnimation(anim), mCharState(state), mSkipAnim(false), mMovingAnim(false), mSecondsOfRunning(0), mSecondsOfSwimming(0) + : mPtr(ptr), mAnimation(anim), mCharState(state), mSkipAnim(false), mMovingAnim(false), + mSecondsOfRunning(0), mSecondsOfSwimming(0), mFallDistance(0), mLastHeight(0) { if(!mAnimation) return; @@ -146,6 +150,7 @@ void CharacterController::update(float duration, Movement &movement) const MWWorld::Class &cls = MWWorld::Class::get(mPtr); bool onground = world->isOnGround(mPtr); + bool gravity = world->isGravity(mPtr); bool inwater = world->isSwimming(mPtr); bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run); bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); @@ -153,6 +158,36 @@ void CharacterController::update(float duration, Movement &movement) const Ogre::Vector3 &rot = cls.getRotationVector(mPtr); speed = cls.getSpeed(mPtr); + //player is falling down + if(!onground && gravity && mLastHeight>mPtr.getRefData().getPosition().pos[2]) + mFallDistance+=(mLastHeight-mPtr.getRefData().getPosition().pos[2]); + + //player just finished falling + if(onground && gravity && mFallDistance>0) + { + float healthLost = cls.getFallDamage(mPtr, mFallDistance); + + if(healthLost>0.0f) + { + DynamicStat health = cls.getCreatureStats(mPtr).getHealth(); + int current = health.getCurrent(); + float realHealthLost = healthLost * (1.0f - (0.25 * 1.25f/*fatigueTerm*/)); + health.setModified (health.getModified()-static_cast(realHealthLost), 0); + health.setCurrent (current-static_cast(realHealthLost)); + cls.getCreatureStats(mPtr).setHealth (health); + MWWorld::Class::get(mPtr).skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); + + const float acrobaticsSkill = cls.getNpcStats (mPtr).getSkill (ESM::Skill::Acrobatics).getModified(); + if(acrobaticsSkill*1.25f/*fatigueTerm*/ 0 && mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) { @@ -176,10 +211,25 @@ void CharacterController::update(float duration, Movement &movement) } } - /* FIXME: The state should be set to Jump, and X/Y movement should be disallowed except - * for the initial thrust (which would be carried by "physics" until landing). */ - if(onground && vec.z > 0.0f) + // FIXME: X/Y movement should be disallowed except for the initial thrust (which would be carried by "physics" until landing). + if(onground && gravity && vec.z > 0.0f) { + //Advance acrobatics on jump, decrease fatigue + if(getState()!=CharState_Jump) + { + setState(CharState_Jump, true); + MWWorld::Class::get(mPtr).skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0); + + const float normalizedEncumbrance = cls.getEncumbrance(mPtr) / cls.getCapacity(mPtr); + const float fatigueJumpBase = MWBase::Environment::get().getWorld()->getStore().get().find ("fFatigueJumpBase")->getFloat(); + const float fatigueJumpMult = MWBase::Environment::get().getWorld()->getStore().get().find ("fFatigueJumpMult")->getFloat(); + DynamicStat fatigue = cls.getCreatureStats(mPtr).getDynamic(2); + int current = fatigue.getCurrent(); + fatigue.setModified(fatigue.getModified()-static_cast(fatigueJumpBase + ((1 - normalizedEncumbrance) * fatigueJumpMult)), 0); + fatigue.setCurrent(current-static_cast(fatigueJumpBase + ((1 - normalizedEncumbrance) * fatigueJumpMult))); + cls.getCreatureStats(mPtr).setDynamic (2, fatigue); + } + float x = cls.getJump(mPtr); if(vec.x == 0 && vec.y == 0) @@ -192,8 +242,6 @@ void CharacterController::update(float duration, Movement &movement) //movement += Ogre::Vector3(lat.x, lat.y, 1.0f) * x * 0.707f * duration; movement.mPosition[2] += x * 0.707f * duration; } - - //decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult; } if(std::abs(vec.x/2.0f) > std::abs(vec.y) && speed > 0.0f) @@ -260,6 +308,8 @@ void CharacterController::update(float duration, Movement &movement) movement.mPosition[2] += moved.z; } mSkipAnim = false; + + mLastHeight = mPtr.getRefData().getPosition().pos[2]; } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 4ee217d119..ffe4077310 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -83,6 +83,9 @@ class CharacterController float mSecondsOfSwimming; float mSecondsOfRunning; + float mFallDistance; + float mLastHeight; + bool mMovingAnim; public: diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 03a55c7f05..db798537ee 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -132,6 +132,11 @@ namespace MWWorld return 0; } + float Class::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const + { + return 0; + } + float Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { throw std::runtime_error ("class does not support enchanting"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index e203fedc3a..3901ea830f 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -146,6 +146,9 @@ namespace MWWorld virtual float getJump(const MWWorld::Ptr &ptr) const; ///< Return jump velocity (not accounting for movement) + virtual float getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const; + ///< Return amount of health points lost when falling + virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const; ///< Return desired movement. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 403ce37bcd..7c52ac4d7d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1493,6 +1493,13 @@ namespace MWWorld return physactor && physactor->getOnGround(); } + bool World::isGravity(const MWWorld::Ptr &ptr) const + { + RefData &refdata = ptr.getRefData(); + const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + return physactor && physactor->getCollisionMode(); + } + bool World::vanityRotateCamera(float * rot) { return mRendering->vanityRotateCamera(rot); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b051e2b938..eca3ca23eb 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -346,6 +346,7 @@ namespace MWWorld virtual bool isSwimming(const MWWorld::Ptr &object) const; virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; + virtual bool isGravity(const MWWorld::Ptr &ptr) const; virtual void togglePOV() { mRendering->togglePOV(); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 2b59e0aade..b024dd5c67 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -143,7 +143,7 @@ namespace Physic bool PhysicActor::getOnGround() const { - return collisionMode && onGround; + return onGround; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////