From 83872f6bf58f143b2bd97d833b5f0f870cc72766 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 01:42:19 +0100 Subject: [PATCH] Knockdown / hit recovery improvements. Use formula and GMSTs from research wiki for knockdown determination. Hand-to-hand automatically knocks out when fatigue empty. --- apps/openmw/mwclass/npc.cpp | 30 +++++++++++++- apps/openmw/mwclass/npc.hpp | 3 ++ apps/openmw/mwmechanics/character.cpp | 48 +++++++++++------------ apps/openmw/mwmechanics/creaturestats.cpp | 22 ++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 9 ++++- 5 files changed, 85 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4aa27e38a..e08a3c07e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -242,6 +242,9 @@ namespace MWClass fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); fWereWolfRunMult = gmst.find("fWereWolfRunMult"); + fKnockDownMult = gmst.find("fKnockDownMult"); + iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); + iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); inited = true; } @@ -651,7 +654,20 @@ namespace MWClass { MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); } - getCreatureStats(ptr).setAttacked(true);//used in CharacterController + getCreatureStats(ptr).setAttacked(true); + + // Check for knockdown + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() + * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) + { + getCreatureStats(ptr).setKnockedDown(true); + + } + else + getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? if(object.isEmpty()) { @@ -726,6 +742,15 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } + + if (object.isEmpty()) + { + // Hand-to-hand automatically knocks down when running out of fatigue + if (getCreatureStats(ptr).getFatigue().getCurrent() < 0) + { + getCreatureStats(ptr).setKnockedDown(true); + } + } } void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const @@ -1286,4 +1311,7 @@ namespace MWClass const ESM::GameSetting *Npc::fJumpAcroMultiplier; const ESM::GameSetting *Npc::fJumpRunMultiplier; const ESM::GameSetting *Npc::fWereWolfRunMult; + const ESM::GameSetting *Npc::fKnockDownMult; + const ESM::GameSetting *Npc::iKnockDownOddsMult; + const ESM::GameSetting *Npc::iKnockDownOddsBase; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index c39ca42ef..22a9632e8 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -33,6 +33,9 @@ namespace MWClass static const ESM::GameSetting *fJumpAcroMultiplier; static const ESM::GameSetting *fJumpRunMultiplier; static const ESM::GameSetting *fWereWolfRunMult; + static const ESM::GameSetting *fKnockDownMult; + static const ESM::GameSetting *iKnockDownOddsMult; + static const ESM::GameSetting *iKnockDownOddsBase; public: diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 86785ec22..07859d57c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -157,40 +157,40 @@ public: void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { - //hit recoils/knockdown animations handling - if(MWWorld::Class::get(mPtr).isActor()) + // hit recoils/knockdown animations handling + if(mPtr.getClass().isActor()) { - if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) + bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); + bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); + if(mHitState == CharState_None) { - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); - - if(mHitState == CharState_None) + if(knockdown) { - if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) - && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) + mHitState = CharState_KnockDown; + mCurrentHit = sHitList[sHitListSize-1]; + mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); + } + else if (recovery) + { + mHitState = CharState_Hit; + int iHit = rand() % (sHitListSize-1); + mCurrentHit = sHitList[iHit]; + if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) { - mHitState = CharState_KnockDown; - mCurrentHit = sHitList[sHitListSize-1]; - mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); - } - else - { - mHitState = CharState_Hit; - int iHit = rand() % (sHitListSize-1); + //only 3 different hit animations if player is in 1st person + int iHit = rand() % (sHitListSize-3); mCurrentHit = sHitList[iHit]; - if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) - { - //only 3 different hit animations if player is in 1st person - int iHit = rand() % (sHitListSize-3); - mCurrentHit = sHitList[iHit]; - } - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } - else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) + else if(!mAnimation->isPlaying(mCurrentHit)) { mCurrentHit.erase(); + if (knockdown) + mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false); + if (recovery) + mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false); mHitState = CharState_None; } } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index b5b9b7156..aadce499e 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -15,7 +15,7 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), - mFallHeight(0), mRecalcDynamicStats(false) + mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -402,4 +402,24 @@ namespace MWMechanics } return false; } + + void CreatureStats::setKnockedDown(bool value) + { + mKnockdown = value; + } + + bool CreatureStats::getKnockedDown() const + { + return mKnockdown; + } + + void CreatureStats::setHitRecovery(bool value) + { + mHitRecovery = value; + } + + bool CreatureStats::getHitRecovery() const + { + return mHitRecovery; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 6e0804638..1f82a9b5c 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -34,7 +34,9 @@ namespace MWMechanics bool mAlarmed; bool mAttacked; bool mHostile; - bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. + bool mAttackingOrSpell; + bool mKnockdown; + bool mHitRecovery; float mFallHeight; @@ -186,6 +188,11 @@ namespace MWMechanics float getEvasion() const; + void setKnockedDown(bool value); + bool getKnockedDown() const; + void setHitRecovery(bool value); + bool getHitRecovery() const; + void setLastHitObject(const std::string &objectid); const std::string &getLastHitObject() const;