mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-03 23:56:43 +00:00 
			
		
		
		
	refactor: move combat move and attack functions to AiCombatStorage
This commit is contained in:
		
							parent
							
								
									7b207a7954
								
							
						
					
					
						commit
						1d4be08f6e
					
				
					 1 changed files with 352 additions and 330 deletions
				
			
		| 
						 | 
				
			
			@ -27,14 +27,15 @@
 | 
			
		|||
#include "aicombataction.hpp"
 | 
			
		||||
#include "combat.hpp"
 | 
			
		||||
 | 
			
		||||
// forward declarations
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    //chooses an attack depending on probability to avoid uniformity
 | 
			
		||||
    /// Choose an attack depending on probability to avoid uniformity
 | 
			
		||||
    ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement);
 | 
			
		||||
 | 
			
		||||
    /// Get an attack min/max attack duration for current actor's weapon
 | 
			
		||||
    void getMinMaxAttackDuration(const MWWorld::Ptr& actor, float (*fMinMaxDurations)[2]);
 | 
			
		||||
 | 
			
		||||
    /// Calculate direction to aim at the moving target
 | 
			
		||||
    Ogre::Vector3 AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const Ogre::Vector3& vLastTargetPos, 
 | 
			
		||||
        float duration, int weapType, float strength);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,34 +50,15 @@ namespace
 | 
			
		|||
        return -Ogre::Math::ASin(dir.z / len).valueDegrees();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    const float PATHFIND_Z_REACH = 50.0f;
 | 
			
		||||
    // distance at which actor pays more attention to decide whether to shortcut or stick to pathgrid
 | 
			
		||||
    const float PATHFIND_CAUTION_DIST = 500.0f;
 | 
			
		||||
    // distance after which actor (failed previously to shortcut) will try again
 | 
			
		||||
    const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f;
 | 
			
		||||
 | 
			
		||||
    // cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;
 | 
			
		||||
    // magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
 | 
			
		||||
    bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY)
 | 
			
		||||
    {
 | 
			
		||||
        if((to - from).length() >= PATHFIND_CAUTION_DIST || std::abs(from.z - to.z) <= PATHFIND_Z_REACH)
 | 
			
		||||
        {
 | 
			
		||||
            Ogre::Vector3 dir = to - from;
 | 
			
		||||
            dir.z = 0;
 | 
			
		||||
            dir.normalise();
 | 
			
		||||
			float verticalOffset = 200; // instead of '200' here we want the height of the actor
 | 
			
		||||
            Ogre::Vector3 _from = from + dir*offsetXY + Ogre::Vector3::UNIT_Z * verticalOffset;
 | 
			
		||||
 | 
			
		||||
            // cast up-down ray and find height in world space of hit
 | 
			
		||||
            float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1);
 | 
			
		||||
 | 
			
		||||
            if(std::abs(from.z - h) <= PATHFIND_Z_REACH)
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    /// Cast up-down ray to fo pits/obstacles on the way
 | 
			
		||||
    /// \note magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
 | 
			
		||||
    bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace MWMechanics
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +105,19 @@ namespace MWMechanics
 | 
			
		|||
            mMinMaxAttackDurationInitialised(false),
 | 
			
		||||
            mLastTargetPos(0,0,0),
 | 
			
		||||
            mLastActorPos(0,0,0),
 | 
			
		||||
        mMovement(){}    
 | 
			
		||||
            mMovement()
 | 
			
		||||
        {}
 | 
			
		||||
 | 
			
		||||
        void startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack);
 | 
			
		||||
        void updateCombatMove(float duration);
 | 
			
		||||
        void stopCombatMove();
 | 
			
		||||
 | 
			
		||||
        void updateWeaponAttackDurations(const MWWorld::Ptr& actor);
 | 
			
		||||
        void updateAttack(float duration, float attacksPeriod);
 | 
			
		||||
        bool startAttack(bool isDistantCombat, const ESM::Weapon* weapon, float attacksPeriod);
 | 
			
		||||
 | 
			
		||||
        /// Start special run attack taking into account actor-target speeds and weapon characteristics
 | 
			
		||||
        void startRunAttack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float attacksPeriod, float distToTarget, float weapRange);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    AiCombat::AiCombat(const MWWorld::Ptr& actor) :
 | 
			
		||||
| 
						 | 
				
			
			@ -136,9 +130,7 @@ namespace MWMechanics
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    void AiCombat::init()
 | 
			
		||||
    {
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Current AiCombat movement states (as of 0.29.0), ignoring the details of the
 | 
			
		||||
| 
						 | 
				
			
			@ -191,8 +183,6 @@ namespace MWMechanics
 | 
			
		|||
        // get or create temporary storage
 | 
			
		||||
        AiCombatStorage& storage = state.get<AiCombatStorage>();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        //General description
 | 
			
		||||
        if(actor.getClass().getCreatureStats(actor).isDead())
 | 
			
		||||
            return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -209,21 +199,10 @@ namespace MWMechanics
 | 
			
		|||
        const MWWorld::Class& actorClass = actor.getClass();
 | 
			
		||||
        MWBase::World* world = MWBase::Environment::get().getWorld();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //Update every frame
 | 
			
		||||
        bool& combatMove = storage.mCombatMove;
 | 
			
		||||
        float& timerCombatMove = storage.mTimerCombatMove; 
 | 
			
		||||
        storage.updateCombatMove(duration);
 | 
			
		||||
 | 
			
		||||
        MWMechanics::Movement& movement = storage.mMovement;
 | 
			
		||||
        if(combatMove)
 | 
			
		||||
        {
 | 
			
		||||
            timerCombatMove -= duration;
 | 
			
		||||
            if( timerCombatMove <= 0)
 | 
			
		||||
            {
 | 
			
		||||
                timerCombatMove = 0;
 | 
			
		||||
                movement.mPosition[1] = movement.mPosition[0] = 0;
 | 
			
		||||
                combatMove = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        actorClass.getMovementSettings(actor) = movement;
 | 
			
		||||
        actorClass.getMovementSettings(actor).mRotation[0] = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -242,39 +221,12 @@ namespace MWMechanics
 | 
			
		|||
        //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f
 | 
			
		||||
        float attacksPeriod = 1.0f;
 | 
			
		||||
 | 
			
		||||
        ESM::Weapon::AttackType attackType;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        
 | 
			
		||||
        bool& attack = storage.mAttack;
 | 
			
		||||
        bool& readyToAttack = storage.mReadyToAttack;
 | 
			
		||||
        float& timerAttack = storage.mTimerAttack;
 | 
			
		||||
        
 | 
			
		||||
        bool& minMaxAttackDurationInitialised = storage.mMinMaxAttackDurationInitialised;
 | 
			
		||||
        float (&minMaxAttackDuration)[3][2] = storage.mMinMaxAttackDuration;
 | 
			
		||||
        
 | 
			
		||||
        if(readyToAttack)
 | 
			
		||||
        {
 | 
			
		||||
            if (!minMaxAttackDurationInitialised)
 | 
			
		||||
            {
 | 
			
		||||
                // TODO: this must be updated when a different weapon is equipped
 | 
			
		||||
                getMinMaxAttackDuration(actor, minMaxAttackDuration);
 | 
			
		||||
                minMaxAttackDurationInitialised = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (timerAttack < 0) attack = false;
 | 
			
		||||
 | 
			
		||||
            timerAttack -= duration;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            timerAttack = -attacksPeriod;
 | 
			
		||||
            attack = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        actorClass.getCreatureStats(actor).setAttackingOrSpell(attack);
 | 
			
		||||
        
 | 
			
		||||
        // update attack state
 | 
			
		||||
        storage.updateWeaponAttackDurations(actor);
 | 
			
		||||
        storage.updateAttack(duration, attacksPeriod);
 | 
			
		||||
        actorClass.getCreatureStats(actor).setAttackingOrSpell(storage.mAttack);
 | 
			
		||||
 | 
			
		||||
        float& actionCooldown = storage.mActionCooldown;
 | 
			
		||||
        actionCooldown -= duration;
 | 
			
		||||
| 
						 | 
				
			
			@ -383,27 +335,12 @@ namespace MWMechanics
 | 
			
		|||
            weapRange = 150.f;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        float& strength = storage.mStrength;      
 | 
			
		||||
        // start new attack
 | 
			
		||||
        if(readyToAttack)
 | 
			
		||||
        bool newAttack = storage.startAttack(distantCombat, weapon, attacksPeriod);
 | 
			
		||||
 | 
			
		||||
        if (newAttack && actor.getClass().isNpc())
 | 
			
		||||
        {
 | 
			
		||||
            if(timerAttack <= -attacksPeriod)
 | 
			
		||||
            {
 | 
			
		||||
                attack = true; // attack starts just now
 | 
			
		||||
 | 
			
		||||
                if (!distantCombat) attackType = chooseBestAttack(weapon, movement);
 | 
			
		||||
                else attackType = ESM::Weapon::AT_Chop; // cause it's =0
 | 
			
		||||
 | 
			
		||||
                strength = OEngine::Misc::Rng::rollClosedProbability();
 | 
			
		||||
 | 
			
		||||
                // Note: may be 0 for some animations
 | 
			
		||||
                timerAttack = minMaxAttackDuration[attackType][0] + 
 | 
			
		||||
                    (minMaxAttackDuration[attackType][1] - minMaxAttackDuration[attackType][0]) * strength;
 | 
			
		||||
 | 
			
		||||
            //say a provoking combat phrase
 | 
			
		||||
                if (actor.getClass().isNpc())
 | 
			
		||||
                {
 | 
			
		||||
            const MWWorld::ESMStore &store = world->getStore();
 | 
			
		||||
            int chance = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->getInt();
 | 
			
		||||
            if (OEngine::Misc::Rng::roll0to99() < chance)
 | 
			
		||||
| 
						 | 
				
			
			@ -411,9 +348,6 @@ namespace MWMechanics
 | 
			
		|||
                MWBase::Environment::get().getDialogueManager()->say(actor, "attack");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Some notes on meanings of variables:
 | 
			
		||||
| 
						 | 
				
			
			@ -492,7 +426,7 @@ namespace MWMechanics
 | 
			
		|||
            if (distantCombat)
 | 
			
		||||
            {
 | 
			
		||||
                Ogre::Vector3& lastTargetPos = storage.mLastTargetPos;
 | 
			
		||||
                Ogre::Vector3 vAimDir = AimDirToMovingTarget(actor, target, lastTargetPos, tReaction, weaptype, strength);
 | 
			
		||||
                Ogre::Vector3 vAimDir = AimDirToMovingTarget(actor, target, lastTargetPos, tReaction, weaptype, storage.mStrength);
 | 
			
		||||
                lastTargetPos = vTargetPos;
 | 
			
		||||
                movement.mRotation[0] = getXAngleToDir(vAimDir);
 | 
			
		||||
                movement.mRotation[2] = getZAngleToDir(vAimDir);
 | 
			
		||||
| 
						 | 
				
			
			@ -507,31 +441,12 @@ namespace MWMechanics
 | 
			
		|||
            if (followTarget && distToTarget > rangeAttack)
 | 
			
		||||
            {
 | 
			
		||||
                //Close-up combat: just run up on target
 | 
			
		||||
                storage.stopCombatMove();
 | 
			
		||||
                movement.mPosition[1] = 1;
 | 
			
		||||
            }
 | 
			
		||||
            else // (within attack dist)
 | 
			
		||||
            {
 | 
			
		||||
                if(movement.mPosition[0] || movement.mPosition[1])
 | 
			
		||||
                {
 | 
			
		||||
                    timerCombatMove = 0.1f + 0.1f * OEngine::Misc::Rng::rollClosedProbability();
 | 
			
		||||
                    combatMove = true;
 | 
			
		||||
                }
 | 
			
		||||
                // only NPCs are smart enough to use dodge movements
 | 
			
		||||
                else if(actorClass.isNpc() && (!distantCombat || (distantCombat && distToTarget < rangeAttack/2)))
 | 
			
		||||
                {
 | 
			
		||||
                    //apply sideway movement (kind of dodging) with some probability
 | 
			
		||||
                    if (OEngine::Misc::Rng::rollClosedProbability() < 0.25)
 | 
			
		||||
                    {
 | 
			
		||||
                        movement.mPosition[0] = OEngine::Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f;
 | 
			
		||||
                        timerCombatMove = 0.05f + 0.15f * OEngine::Misc::Rng::rollClosedProbability();
 | 
			
		||||
                        combatMove = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if(distantCombat && distToTarget < rangeAttack/4)
 | 
			
		||||
                {
 | 
			
		||||
                    movement.mPosition[1] = -1;
 | 
			
		||||
                }
 | 
			
		||||
                storage.startCombatMove(actorClass.isNpc(), distantCombat, distToTarget, rangeAttack);
 | 
			
		||||
 | 
			
		||||
                readyToAttack = true;
 | 
			
		||||
                //only once got in melee combat, actor is allowed to use close-up shortcutting
 | 
			
		||||
| 
						 | 
				
			
			@ -609,52 +524,24 @@ namespace MWMechanics
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            movement.mPosition[1] = 1;
 | 
			
		||||
            if (readyToAttack)
 | 
			
		||||
            {
 | 
			
		||||
                // to stop possible sideway moving after target moved out of attack range
 | 
			
		||||
                combatMove = true;
 | 
			
		||||
                timerCombatMove = 0;
 | 
			
		||||
            }
 | 
			
		||||
                storage.stopCombatMove();
 | 
			
		||||
                readyToAttack = false;
 | 
			
		||||
            }
 | 
			
		||||
            movement.mPosition[1] = 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!isStuck && distToTarget > rangeAttack && !distantCombat)
 | 
			
		||||
        {
 | 
			
		||||
            // special run attack; it shouldn't affect melee combat tactics
 | 
			
		||||
            if (actorClass.getMovementSettings(actor).mPosition[1] == 1)
 | 
			
		||||
            {
 | 
			
		||||
                /*  check if actor can overcome the distance = distToTarget - attackerWeapRange
 | 
			
		||||
                    less than in time of swinging with weapon (t_swing), then start attacking 
 | 
			
		||||
                */
 | 
			
		||||
                float speed1 = actorClass.getSpeed(actor);
 | 
			
		||||
                float speed2 = target.getClass().getSpeed(target);
 | 
			
		||||
                if(target.getClass().getMovementSettings(target).mPosition[0] == 0
 | 
			
		||||
                        && target.getClass().getMovementSettings(target).mPosition[1] == 0)
 | 
			
		||||
                    speed2 = 0;
 | 
			
		||||
 | 
			
		||||
                float s1 = distToTarget - weapRange;
 | 
			
		||||
                float t = s1/speed1;
 | 
			
		||||
                float s2 = speed2 * t;
 | 
			
		||||
                float t_swing = 
 | 
			
		||||
                    minMaxAttackDuration[ESM::Weapon::AT_Thrust][0] + 
 | 
			
		||||
                    (minMaxAttackDuration[ESM::Weapon::AT_Thrust][1] - minMaxAttackDuration[ESM::Weapon::AT_Thrust][0]) * OEngine::Misc::Rng::rollClosedProbability();
 | 
			
		||||
 | 
			
		||||
                if (t + s2/speed1 <= t_swing)
 | 
			
		||||
                {
 | 
			
		||||
                    readyToAttack = true;
 | 
			
		||||
                    if(timerAttack <= -attacksPeriod)
 | 
			
		||||
                    {
 | 
			
		||||
                        timerAttack = t_swing;
 | 
			
		||||
                        attack = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                storage.startRunAttack(actor, target, attacksPeriod, distToTarget, weapRange);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // NOTE: This section gets updated every tReaction, which is currently hard
 | 
			
		||||
        //       coded at 250ms or 1/4 second
 | 
			
		||||
        //
 | 
			
		||||
        // TODO: Add a parameter to vary DURATION_SAME_SPOT?
 | 
			
		||||
        if((distToTarget > rangeAttack || followTarget) &&
 | 
			
		||||
            mObstacleCheck.check(actor, tReaction)) // check if evasive action needed
 | 
			
		||||
| 
						 | 
				
			
			@ -749,12 +636,128 @@ namespace MWMechanics
 | 
			
		|||
        package.mPackage = combat.release();
 | 
			
		||||
        sequence.mPackages.push_back(package);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void AiCombatStorage::startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack)
 | 
			
		||||
    {
 | 
			
		||||
        if (mMovement.mPosition[0] || mMovement.mPosition[1])
 | 
			
		||||
        {
 | 
			
		||||
            mTimerCombatMove = 0.1f + 0.1f * OEngine::Misc::Rng::rollClosedProbability();
 | 
			
		||||
            mCombatMove = true;
 | 
			
		||||
        }
 | 
			
		||||
        // only NPCs are smart enough to use dodge movements
 | 
			
		||||
        else if (isNpc && (!isDistantCombat || (isDistantCombat && distToTarget < rangeAttack/2)))
 | 
			
		||||
        {
 | 
			
		||||
            //apply sideway movement (kind of dodging) with some probability
 | 
			
		||||
            if (OEngine::Misc::Rng::rollClosedProbability() < 0.25)
 | 
			
		||||
            {
 | 
			
		||||
                mMovement.mPosition[0] = OEngine::Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f;
 | 
			
		||||
                mTimerCombatMove = 0.05f + 0.15f * OEngine::Misc::Rng::rollClosedProbability();
 | 
			
		||||
                mCombatMove = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isDistantCombat && distToTarget < rangeAttack/4)
 | 
			
		||||
        {
 | 
			
		||||
            mMovement.mPosition[1] = -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void AiCombatStorage::updateCombatMove(float duration)
 | 
			
		||||
    {
 | 
			
		||||
        if (!mCombatMove) return;
 | 
			
		||||
 | 
			
		||||
        mTimerCombatMove -= duration;
 | 
			
		||||
        if (mTimerCombatMove <= 0)
 | 
			
		||||
        {
 | 
			
		||||
            stopCombatMove();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void AiCombatStorage::stopCombatMove()
 | 
			
		||||
    {
 | 
			
		||||
        mCombatMove = false;
 | 
			
		||||
        mTimerCombatMove = 0;
 | 
			
		||||
        mMovement.mPosition[1] = mMovement.mPosition[0] = 0;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void AiCombatStorage::updateWeaponAttackDurations(const MWWorld::Ptr& actor)
 | 
			
		||||
    {
 | 
			
		||||
        if (mReadyToAttack && !mMinMaxAttackDurationInitialised)
 | 
			
		||||
        {
 | 
			
		||||
            // TODO: this must be updated when a different weapon is equipped
 | 
			
		||||
            getMinMaxAttackDuration(actor, mMinMaxAttackDuration);
 | 
			
		||||
            mMinMaxAttackDurationInitialised = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void AiCombatStorage::updateAttack(float duration, float attacksPeriod)
 | 
			
		||||
    {
 | 
			
		||||
        if (mReadyToAttack)
 | 
			
		||||
        {
 | 
			
		||||
            if (mTimerAttack < 0) mAttack = false;
 | 
			
		||||
            mTimerAttack -= duration;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            mTimerAttack = -attacksPeriod;
 | 
			
		||||
            mAttack = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool AiCombatStorage::startAttack(bool isDistantCombat, const ESM::Weapon* weapon, float attacksPeriod)
 | 
			
		||||
    {
 | 
			
		||||
        if (!mReadyToAttack || mTimerAttack > -attacksPeriod)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        // start new attack
 | 
			
		||||
        mAttack = true;
 | 
			
		||||
 | 
			
		||||
        ESM::Weapon::AttackType attackType;
 | 
			
		||||
 | 
			
		||||
        if (!isDistantCombat)
 | 
			
		||||
            attackType = chooseBestAttack(weapon, mMovement);
 | 
			
		||||
        else 
 | 
			
		||||
            attackType = ESM::Weapon::AT_Chop; // cause it's =0
 | 
			
		||||
 | 
			
		||||
        mStrength = OEngine::Misc::Rng::rollClosedProbability();
 | 
			
		||||
 | 
			
		||||
        // Note: may be 0 for some animations
 | 
			
		||||
        mTimerAttack = mMinMaxAttackDuration[attackType][0] + 
 | 
			
		||||
            (mMinMaxAttackDuration[attackType][1] - mMinMaxAttackDuration[attackType][0]) * mStrength;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void AiCombatStorage::startRunAttack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float attacksPeriod, float distToTarget, float weapRange)
 | 
			
		||||
    {
 | 
			
		||||
        float actorSpeed = actor.getClass().getSpeed(actor);
 | 
			
		||||
        float targetSpeed = target.getClass().getSpeed(target);
 | 
			
		||||
 | 
			
		||||
        if (target.getClass().getMovementSettings(target).mPosition[0] == 0 
 | 
			
		||||
                && target.getClass().getMovementSettings(target).mPosition[1] == 0)
 | 
			
		||||
            targetSpeed = 0;
 | 
			
		||||
 | 
			
		||||
        float distToOvercome = distToTarget - weapRange;
 | 
			
		||||
        float t_toOvercome = distToOvercome/actorSpeed;
 | 
			
		||||
        float distTargetWillOvercome = targetSpeed * t_toOvercome;
 | 
			
		||||
        float t_weap_swing = mMinMaxAttackDuration[ESM::Weapon::AT_Thrust][0] + 
 | 
			
		||||
            (mMinMaxAttackDuration[ESM::Weapon::AT_Thrust][1] - mMinMaxAttackDuration[ESM::Weapon::AT_Thrust][0]) * OEngine::Misc::Rng::rollClosedProbability();
 | 
			
		||||
 | 
			
		||||
        if (t_toOvercome + distTargetWillOvercome/actorSpeed <= t_weap_swing)
 | 
			
		||||
        {
 | 
			
		||||
            mReadyToAttack = true;
 | 
			
		||||
            if (mTimerAttack <= -attacksPeriod)
 | 
			
		||||
            {
 | 
			
		||||
                mTimerAttack = t_weap_swing;
 | 
			
		||||
                mAttack = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement)
 | 
			
		||||
    {
 | 
			
		||||
        ESM::Weapon::AttackType attackType;
 | 
			
		||||
| 
						 | 
				
			
			@ -932,4 +935,23 @@ Ogre::Vector3 AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr
 | 
			
		|||
        return vTargetPos + vTargetMoveDir * t_collision - vActorPos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY)
 | 
			
		||||
    {
 | 
			
		||||
        if((to - from).length() >= PATHFIND_CAUTION_DIST || std::abs(from.z - to.z) <= PATHFIND_Z_REACH)
 | 
			
		||||
        {
 | 
			
		||||
            Ogre::Vector3 dir = to - from;
 | 
			
		||||
            dir.z = 0;
 | 
			
		||||
            dir.normalise();
 | 
			
		||||
		    float verticalOffset = 200; // instead of '200' here we want the height of the actor
 | 
			
		||||
            Ogre::Vector3 _from = from + dir*offsetXY + Ogre::Vector3::UNIT_Z * verticalOffset;
 | 
			
		||||
 | 
			
		||||
            // cast up-down ray and find height in world space of hit
 | 
			
		||||
            float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1);
 | 
			
		||||
 | 
			
		||||
            if(std::abs(from.z - h) <= PATHFIND_Z_REACH)
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue