mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 07:56:40 +00:00 
			
		
		
		
	pathgrid shortcutting extended
This commit is contained in:
		
							parent
							
								
									4e7ee97050
								
							
						
					
					
						commit
						f811abb752
					
				
					 9 changed files with 181 additions and 77 deletions
				
			
		| 
						 | 
					@ -407,6 +407,8 @@ namespace MWBase
 | 
				
			||||||
            virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0;
 | 
					            virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0;
 | 
				
			||||||
            ///< get Line of Sight (morrowind stupid implementation)
 | 
					            ///< get Line of Sight (morrowind stupid implementation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0;
 | 
					            virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            virtual int canRest() = 0;
 | 
					            virtual int canRest() = 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,40 @@ namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //chooses an attack depending on probability to avoid uniformity
 | 
					    //chooses an attack depending on probability to avoid uniformity
 | 
				
			||||||
    void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement);
 | 
					    void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float getZAngleToDir(const Ogre::Vector3& dir, float dirLen = 0.0f)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        float len = (dirLen >= 0.0f)? dirLen : dir.length();
 | 
				
			||||||
 | 
					        return Ogre::Radian( Ogre::Math::ACos(dir.y / len) * sgn(Ogre::Math::ASin(dir.x / 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 offset)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if((to - from).length() >= PATHFIND_CAUTION_DIST || 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*offset + 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(abs(from.z - h) <= PATHFIND_Z_REACH)
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MWMechanics
 | 
					namespace MWMechanics
 | 
				
			||||||
| 
						 | 
					@ -44,9 +78,10 @@ namespace MWMechanics
 | 
				
			||||||
        mReadyToAttack(false),
 | 
					        mReadyToAttack(false),
 | 
				
			||||||
        mStrike(false),
 | 
					        mStrike(false),
 | 
				
			||||||
        mCombatMove(false),
 | 
					        mCombatMove(false),
 | 
				
			||||||
        mRotate(false),
 | 
					 | 
				
			||||||
        mMovement(),
 | 
					        mMovement(),
 | 
				
			||||||
        mTargetAngle(0)
 | 
					        mTargetAngle(0),
 | 
				
			||||||
 | 
					        mForceNoShortcut(false),
 | 
				
			||||||
 | 
					        mShortcutFailPos()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -74,10 +109,9 @@ namespace MWMechanics
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        actor.getClass().getMovementSettings(actor) = mMovement;
 | 
					        actor.getClass().getMovementSettings(actor) = mMovement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (mRotate)
 | 
					        if(actor.getRefData().getPosition().rot[2] != mTargetAngle)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (zTurn(actor, Ogre::Degree(mTargetAngle)))
 | 
					            zTurn(actor, Ogre::Degree(mTargetAngle));
 | 
				
			||||||
                mRotate = false;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -136,6 +170,7 @@ namespace MWMechanics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
 | 
					        actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get weapon characteristics
 | 
				
			||||||
        if (actor.getClass().hasInventoryStore(actor))
 | 
					        if (actor.getClass().hasInventoryStore(actor))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState();
 | 
					            MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState();
 | 
				
			||||||
| 
						 | 
					@ -166,15 +201,13 @@ namespace MWMechanics
 | 
				
			||||||
            weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit)
 | 
					            weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ESM::Position pos = actor.getRefData().getPosition();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        float rangeMelee;
 | 
					        float rangeMelee;
 | 
				
			||||||
        float rangeCloseUp;
 | 
					        float rangeCloseUp;
 | 
				
			||||||
        bool distantCombat = false;
 | 
					        bool distantCombat = false;
 | 
				
			||||||
        if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_Thrown)
 | 
					        if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_Thrown)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            rangeMelee = 1000; // TODO: should depend on archer skill
 | 
					            rangeMelee = 1000; // TODO: should depend on archer skill
 | 
				
			||||||
            rangeCloseUp = 0; //doesn't needed when attacking from distance
 | 
					            rangeCloseUp = 0; // not needed in ranged combat
 | 
				
			||||||
            distantCombat = true;
 | 
					            distantCombat = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
| 
						 | 
					@ -183,22 +216,26 @@ namespace MWMechanics
 | 
				
			||||||
            rangeCloseUp = 300;
 | 
					            rangeCloseUp = 300;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Ogre::Vector3 vStart(pos.pos[0], pos.pos[1], pos.pos[2]);
 | 
					        ESM::Position pos = actor.getRefData().getPosition();
 | 
				
			||||||
        ESM::Position targetPos = mTarget.getRefData().getPosition();
 | 
					        Ogre::Vector3 vActorPos(pos.pos); 
 | 
				
			||||||
        Ogre::Vector3 vDest(targetPos.pos[0], targetPos.pos[1], targetPos.pos[2]);
 | 
					        Ogre::Vector3 vTargetPos(mTarget.getRefData().getPosition().pos);
 | 
				
			||||||
        Ogre::Vector3 vDir = vDest - vStart;
 | 
					        Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos;
 | 
				
			||||||
        float distBetween = vDir.length();
 | 
					        float distToTarget = vDirToTarget.length();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mFollowTarget) )
 | 
					        bool isStuck = false;
 | 
				
			||||||
 | 
					        float speed = 0.0f;
 | 
				
			||||||
 | 
							if(mMovement.mPosition[1] && (Ogre::Vector3(mLastPos.pos) - vActorPos).length() < (speed = cls.getSpeed(actor)) / 10.0f) 
 | 
				
			||||||
 | 
					            isStuck = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mLastPos = pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(distToTarget < rangeMelee || (distToTarget <= rangeCloseUp && mFollowTarget && !isStuck) )
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //Melee and Close-up combat
 | 
					            //Melee and Close-up combat
 | 
				
			||||||
            vDir.z = 0;
 | 
					            vDirToTarget.z = 0;
 | 
				
			||||||
            float dirLen = vDir.length();
 | 
					            mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
 | 
				
			||||||
            mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees();
 | 
					 | 
				
			||||||
            mRotate = true;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget);
 | 
					            if (mFollowTarget && distToTarget > rangeMelee)
 | 
				
			||||||
            if (mFollowTarget && distBetween > rangeMelee)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //Close-up combat: just run up on target
 | 
					                //Close-up combat: just run up on target
 | 
				
			||||||
                mMovement.mPosition[1] = 1;
 | 
					                mMovement.mPosition[1] = 1;
 | 
				
			||||||
| 
						 | 
					@ -228,7 +265,7 @@ namespace MWMechanics
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if(distantCombat && distBetween < rangeMelee/4)
 | 
					                if(distantCombat && distToTarget < rangeMelee/4)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    mMovement.mPosition[1] = -1;
 | 
					                    mMovement.mPosition[1] = -1;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -238,32 +275,80 @@ namespace MWMechanics
 | 
				
			||||||
                mFollowTarget = true;
 | 
					                mFollowTarget = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else
 | 
					        else // remote pathfinding
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //target is at far distance: build path to target OR follow target (if previously actor had reached it once)
 | 
					            bool preferShortcut = false;
 | 
				
			||||||
            mFollowTarget = false;
 | 
					            bool inLOS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            buildNewPath(actor); //may fail to build a path, check before use
 | 
					            if(mReadyToAttack) isStuck = false;
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
            //delete visited path node
 | 
					            // check if shortcut is available
 | 
				
			||||||
            mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]);
 | 
					            if(!isStuck 
 | 
				
			||||||
 | 
					                && (!mForceNoShortcut
 | 
				
			||||||
            //if no new path leave mTargetAngle unchanged
 | 
					                || (Ogre::Vector3(mShortcutFailPos.pos) - vActorPos).length() >= PATHFIND_SHORTCUT_RETRY_DIST)
 | 
				
			||||||
            if(!mPathFinder.getPath().empty())
 | 
					                && (inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //try shortcut
 | 
					                if(speed == 0.0f) speed = cls.getSpeed(actor);
 | 
				
			||||||
                if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget))
 | 
					                // maximum dist before pit/obstacle for actor to avoid them depending on his speed
 | 
				
			||||||
                    mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees();
 | 
					                float maxAvoidDist = tReaction * speed + speed / MAX_VEL_ANGULAR.valueRadians() * 2;
 | 
				
			||||||
                else
 | 
					                //if(actor.getRefData().getPosition().rot[2] != mTargetAngle)
 | 
				
			||||||
                    mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
 | 
									preferShortcut = checkWayIsClear(vActorPos, vTargetPos, distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2);
 | 
				
			||||||
                mRotate = true;
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if(preferShortcut)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
					                mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
 | 
				
			||||||
 | 
					                mForceNoShortcut = false;
 | 
				
			||||||
 | 
					                mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0;
 | 
				
			||||||
 | 
					                if(mPathFinder.isPathConstructed())
 | 
				
			||||||
 | 
					                    mPathFinder.clearPath();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else // if shortcut failed stick to path grid
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if(!isStuck && mShortcutFailPos.pos[0] == 0.0f && mShortcutFailPos.pos[1] == 0.0f && mShortcutFailPos.pos[2] == 0.0f)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    mForceNoShortcut = true;
 | 
				
			||||||
 | 
					                    mShortcutFailPos = pos;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                mFollowTarget = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                buildNewPath(actor); //may fail to build a path, check before use
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //delete visited path node
 | 
				
			||||||
 | 
					                mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // This works on the borders between the path grid and areas with no waypoints.
 | 
				
			||||||
 | 
					                if(inLOS && mPathFinder.getPath().size() > 1)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // get point just before target
 | 
				
			||||||
 | 
					                    std::list<ESM::Pathgrid::Point>::iterator pntIter = --mPathFinder.getPath().end();
 | 
				
			||||||
 | 
					                    --pntIter;
 | 
				
			||||||
 | 
									    Ogre::Vector3 vBeforeTarget = Ogre::Vector3(pntIter->mX, pntIter->mY, pntIter->mZ);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // if current actor pos is closer to target then last point of path (excluding target itself) then go straight on target
 | 
				
			||||||
 | 
					                    if(distToTarget <= (vTargetPos - vBeforeTarget).length())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
 | 
				
			||||||
 | 
					                        preferShortcut = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // if there is no new path, then go straight on target
 | 
				
			||||||
 | 
					                if(!preferShortcut)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if(!mPathFinder.getPath().empty())
 | 
				
			||||||
 | 
					                        mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
 | 
				
			||||||
 | 
					                    else 
 | 
				
			||||||
 | 
					                        mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            mMovement.mPosition[1] = 1;
 | 
					            mMovement.mPosition[1] = 1;
 | 
				
			||||||
            mReadyToAttack = false;
 | 
					            mReadyToAttack = false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(distBetween > rangeMelee)
 | 
					        if(distToTarget > rangeMelee)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //special run attack; it shouldn't affect melee combat tactics
 | 
					            //special run attack; it shouldn't affect melee combat tactics
 | 
				
			||||||
            if(actor.getClass().getMovementSettings(actor).mPosition[1] == 1)
 | 
					            if(actor.getClass().getMovementSettings(actor).mPosition[1] == 1)
 | 
				
			||||||
| 
						 | 
					@ -277,7 +362,7 @@ namespace MWMechanics
 | 
				
			||||||
                        && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0)
 | 
					                        && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0)
 | 
				
			||||||
                    speed2 = 0;
 | 
					                    speed2 = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                float s1 = distBetween - weapRange;
 | 
					                float s1 = distToTarget - weapRange;
 | 
				
			||||||
                float t = s1/speed1;
 | 
					                float t = s1/speed1;
 | 
				
			||||||
                float s2 = speed2 * t;
 | 
					                float s2 = speed2 * t;
 | 
				
			||||||
                float t_swing = 0.17f/weapSpeed;//instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags
 | 
					                float t_swing = 0.17f/weapSpeed;//instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags
 | 
				
			||||||
| 
						 | 
					@ -300,31 +385,23 @@ namespace MWMechanics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void AiCombat::buildNewPath(const MWWorld::Ptr& actor)
 | 
					    void AiCombat::buildNewPath(const MWWorld::Ptr& actor)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        //Construct path to target
 | 
					        Ogre::Vector3 newPathTarget = Ogre::Vector3(mTarget.getRefData().getPosition().pos);
 | 
				
			||||||
        ESM::Pathgrid::Point dest;
 | 
					
 | 
				
			||||||
        dest.mX = mTarget.getRefData().getPosition().pos[0];
 | 
					        float dist;
 | 
				
			||||||
        dest.mY = mTarget.getRefData().getPosition().pos[1];
 | 
					 | 
				
			||||||
        dest.mZ = mTarget.getRefData().getPosition().pos[2];
 | 
					 | 
				
			||||||
        Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ);
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        float dist = -1; //hack to indicate first time, to construct a new path
 | 
					 | 
				
			||||||
        if(!mPathFinder.getPath().empty())
 | 
					        if(!mPathFinder.getPath().empty())
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back();
 | 
					            ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back();
 | 
				
			||||||
            Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ);
 | 
					            Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ);
 | 
				
			||||||
            dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length());
 | 
					            dist = (newPathTarget - currPathTarget).length();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        else dist = 1e+38F; // necessarily construct a new path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        float targetPosThreshold;
 | 
					        float targetPosThreshold = (actor.getCell()->getCell()->isExterior())? 300 : 100;
 | 
				
			||||||
        bool isOutside = actor.getCell()->getCell()->isExterior();
 | 
					 | 
				
			||||||
        if (isOutside)
 | 
					 | 
				
			||||||
            targetPosThreshold = 300;
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            targetPosThreshold = 100;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if((dist < 0) || (dist > targetPosThreshold))
 | 
					        //construct new path only if target has moved away more than on [targetPosThreshold]
 | 
				
			||||||
 | 
					        if(dist > targetPosThreshold)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //construct new path only if target has moved away more than on <targetPosThreshold>
 | 
					 | 
				
			||||||
            ESM::Position pos = actor.getRefData().getPosition();
 | 
					            ESM::Position pos = actor.getRefData().getPosition();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ESM::Pathgrid::Point start;
 | 
					            ESM::Pathgrid::Point start;
 | 
				
			||||||
| 
						 | 
					@ -332,17 +409,18 @@ namespace MWMechanics
 | 
				
			||||||
            start.mY = pos.pos[1];
 | 
					            start.mY = pos.pos[1];
 | 
				
			||||||
            start.mZ = pos.pos[2];
 | 
					            start.mZ = pos.pos[2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ESM::Pathgrid::Point dest;
 | 
				
			||||||
 | 
					            dest.mX = newPathTarget.x;
 | 
				
			||||||
 | 
					            dest.mY = newPathTarget.y;
 | 
				
			||||||
 | 
					            dest.mZ = newPathTarget.z;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(!mPathFinder.isPathConstructed())
 | 
					            if(!mPathFinder.isPathConstructed())
 | 
				
			||||||
                mPathFinder.buildPath(start, dest, actor.getCell(), isOutside);
 | 
					                mPathFinder.buildPath(start, dest, actor.getCell(), false);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                PathFinder newPathFinder;
 | 
					                PathFinder newPathFinder;
 | 
				
			||||||
                newPathFinder.buildPath(start, dest, actor.getCell(), isOutside);
 | 
					                newPathFinder.buildPath(start, dest, actor.getCell(), false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                //TO EXPLORE:
 | 
					 | 
				
			||||||
                //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path,
 | 
					 | 
				
			||||||
                //not the actual path length. Here we should know if the new path is actually more effective.
 | 
					 | 
				
			||||||
                //if(pathFinder2.getPathSize() < mPathFinder.getPathSize())
 | 
					 | 
				
			||||||
                if(!mPathFinder.getPath().empty())
 | 
					                if(!mPathFinder.getPath().empty())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    newPathFinder.syncStart(mPathFinder.getPath());
 | 
					                    newPathFinder.syncStart(mPathFinder.getPath());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,8 +43,11 @@ namespace MWMechanics
 | 
				
			||||||
            bool mReadyToAttack, mStrike;
 | 
					            bool mReadyToAttack, mStrike;
 | 
				
			||||||
            bool mFollowTarget;
 | 
					            bool mFollowTarget;
 | 
				
			||||||
            bool mCombatMove;
 | 
					            bool mCombatMove;
 | 
				
			||||||
            bool mRotate;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            bool mForceNoShortcut;
 | 
				
			||||||
 | 
					            ESM::Position mShortcutFailPos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ESM::Position mLastPos;
 | 
				
			||||||
            MWMechanics::Movement mMovement;
 | 
					            MWMechanics::Movement mMovement;
 | 
				
			||||||
            MWWorld::Ptr mTarget;
 | 
					            MWWorld::Ptr mTarget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -384,22 +384,21 @@ namespace MWMechanics
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void PathFinder::syncStart(const std::list<ESM::Pathgrid::Point> &path)
 | 
					    bool PathFinder::syncStart(const std::list<ESM::Pathgrid::Point> &path)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (mPath.size() < 2)
 | 
					        if (mPath.size() < 2)
 | 
				
			||||||
            return; //nothing to pop
 | 
					            return false; //nothing to pop
 | 
				
			||||||
        std::list<ESM::Pathgrid::Point>::const_iterator oldStart = path.begin();
 | 
					        std::list<ESM::Pathgrid::Point>::const_iterator oldStart = path.begin();
 | 
				
			||||||
        std::list<ESM::Pathgrid::Point>::iterator iter = ++mPath.begin();
 | 
					        std::list<ESM::Pathgrid::Point>::iterator iter = ++mPath.begin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(    (*iter).mX == oldStart->mX
 | 
					        if(    (*iter).mX == oldStart->mX
 | 
				
			||||||
            && (*iter).mY == oldStart->mY
 | 
					            && (*iter).mY == oldStart->mY
 | 
				
			||||||
            && (*iter).mZ == oldStart->mZ
 | 
					            && (*iter).mZ == oldStart->mZ)
 | 
				
			||||||
            && (*iter).mAutogenerated == oldStart->mAutogenerated
 | 
					 | 
				
			||||||
            && (*iter).mConnectionNum == oldStart->mConnectionNum )
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            mPath.pop_front();
 | 
					            mPath.pop_front();
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,10 +64,15 @@ namespace MWMechanics
 | 
				
			||||||
                return mPath;
 | 
					                return mPath;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //When first point of newly created path is the nearest to actor point, then
 | 
					            /** Synchronize new path with old one to avoid visiting 1 waypoint 2 times
 | 
				
			||||||
            //the cituation can occure when this point is undesirable (if the 2nd point of new path == the 1st point of old path)
 | 
					            @note
 | 
				
			||||||
            //This functions deletes that point.
 | 
					                If the first point is chosen as the nearest one
 | 
				
			||||||
            void syncStart(const std::list<ESM::Pathgrid::Point> &path);
 | 
					                the cituation can occure when the 1st point of the new path is undesirable
 | 
				
			||||||
 | 
					                (i.e. the 2nd point of new path == the 1st point of old path).
 | 
				
			||||||
 | 
					            @param path - old path
 | 
				
			||||||
 | 
					            @return true if such point was found and deleted
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
					            bool syncStart(const std::list<ESM::Pathgrid::Point> &path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            void addPointToPath(ESM::Pathgrid::Point &point)
 | 
					            void addPointToPath(ESM::Pathgrid::Point &point)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,8 +31,7 @@ bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle)
 | 
				
			||||||
    if (absDiff < epsilon)
 | 
					    if (absDiff < epsilon)
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Max. speed of 10 radian per sec
 | 
					    Ogre::Radian limit = MAX_VEL_ANGULAR * MWBase::Environment::get().getFrameDuration();
 | 
				
			||||||
    Ogre::Radian limit = Ogre::Radian(10) * MWBase::Environment::get().getFrameDuration();
 | 
					 | 
				
			||||||
    if (absDiff > limit)
 | 
					    if (absDiff > limit)
 | 
				
			||||||
        diff = Ogre::Math::Sign(diff) * limit;
 | 
					        diff = Ogre::Math::Sign(diff) * limit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,9 @@ class Ptr;
 | 
				
			||||||
namespace MWMechanics
 | 
					namespace MWMechanics
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Max rotating speed, radian/sec
 | 
				
			||||||
 | 
					const Ogre::Radian MAX_VEL_ANGULAR(10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// configure rotation settings for an actor to reach this target angle (eventually)
 | 
					/// configure rotation settings for an actor to reach this target angle (eventually)
 | 
				
			||||||
/// @return have we reached the target angle?
 | 
					/// @return have we reached the target angle?
 | 
				
			||||||
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle);
 | 
					bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1893,6 +1893,19 @@ namespace MWWorld
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float World::getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        btVector3 btFrom(from.x, from.y, from.z);
 | 
				
			||||||
 | 
					        btVector3 btTo = btVector3(dir.x, dir.y, dir.z);
 | 
				
			||||||
 | 
					        btTo.normalize();
 | 
				
			||||||
 | 
					        btTo = btFrom + btTo * maxDist;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::pair<std::string, float> result = mPhysEngine->rayTest(btFrom, btTo, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(result.second == -1) return maxDist;
 | 
				
			||||||
 | 
					        else return result.second*(btTo-btFrom).length();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable)
 | 
					    void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        OEngine::Physic::PhysicActor *physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle());
 | 
					        OEngine::Physic::PhysicActor *physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -510,6 +510,8 @@ namespace MWWorld
 | 
				
			||||||
            virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc);
 | 
					            virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc);
 | 
				
			||||||
            ///< get Line of Sight (morrowind stupid implementation)
 | 
					            ///< get Line of Sight (morrowind stupid implementation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable);
 | 
					            virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            virtual int canRest();
 | 
					            virtual int canRest();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue