    001    package net.minecraft.src;
    002
    003    public class PathNavigate
    004    {
    005        private EntityLiving theEntity;
    006        private World worldObj;
    007
    008        /** The PathEntity being followed. */
    009        private PathEntity currentPath;
    010        private float speed;
    011
    012        /**
    013         * The number of blocks (extra) +/- in each axis that get pulled out as cache for the pathfinder's search space
    014         */
    015        private float pathSearchRange;
    016        private boolean noSunPathfind = false;
    017
    018        /** Time, in number of ticks, following the current path */
    019        private int totalTicks;
    020
    021        /**
    022         * The time when the last position check was done (to detect successful movement)
    023         */
    024        private int ticksAtLastPos;
    025
    026        /**
    027         * Coordinates of the entity's position last time a check was done (part of monitoring getting 'stuck')
    028         */
    029        private Vec3 lastPosCheck = Vec3.createVectorHelper(0.0D, 0.0D, 0.0D);
    030
    031        /**
    032         * Specifically, if a wooden door block is even considered to be passable by the pathfinder
    033         */
    034        private boolean canPassOpenWoodenDoors = true;
    035
    036        /** If door blocks are considered passable even when closed */
    037        private boolean canPassClosedWoodenDoors = false;
    038
    039        /** If water blocks are avoided (at least by the pathfinder) */
    040        private boolean avoidsWater = false;
    041
    042        /**
    043         * If the entity can swim. Swimming AI enables this and the pathfinder will also cause the entity to swim straight
    044         * upwards when underwater
    045         */
    046        private boolean canSwim = false;
    047
    048        public PathNavigate(EntityLiving par1EntityLiving, World par2World, float par3)
    049        {
    050            this.theEntity = par1EntityLiving;
    051            this.worldObj = par2World;
    052            this.pathSearchRange = par3;
    053        }
    054
    055        public void setAvoidsWater(boolean par1)
    056        {
    057            this.avoidsWater = par1;
    058        }
    059
    060        public boolean getAvoidsWater()
    061        {
    062            return this.avoidsWater;
    063        }
    064
    065        public void setBreakDoors(boolean par1)
    066        {
    067            this.canPassClosedWoodenDoors = par1;
    068        }
    069
    070        /**
    071         * Sets if the entity can enter open doors
    072         */
    073        public void setEnterDoors(boolean par1)
    074        {
    075            this.canPassOpenWoodenDoors = par1;
    076        }
    077
    078        /**
    079         * Returns true if the entity can break doors, false otherwise
    080         */
    081        public boolean getCanBreakDoors()
    082        {
    083            return this.canPassClosedWoodenDoors;
    084        }
    085
    086        /**
    087         * Sets if the path should avoid sunlight
    088         */
    089        public void setAvoidSun(boolean par1)
    090        {
    091            this.noSunPathfind = par1;
    092        }
    093
    094        /**
    095         * Sets the speed
    096         */
    097        public void setSpeed(float par1)
    098        {
    099            this.speed = par1;
    100        }
    101
    102        /**
    103         * Sets if the entity can swim
    104         */
    105        public void setCanSwim(boolean par1)
    106        {
    107            this.canSwim = par1;
    108        }
    109
    110        /**
    111         * Returns the path to the given coordinates
    112         */
    113        public PathEntity getPathToXYZ(double par1, double par3, double par5)
    114        {
    115            return !this.canNavigate() ? null : this.worldObj.getEntityPathToXYZ(this.theEntity, MathHelper.floor_double(par1), (int)par3, MathHelper.floor_double(par5), this.pathSearchRange, this.canPassOpenWoodenDoors, this.canPassClosedWoodenDoors, this.avoidsWater, this.canSwim);
    116        }
    117
    118        /**
    119         * Try to find and set a path to XYZ. Returns true if successful.
    120         */
    121        public boolean tryMoveToXYZ(double par1, double par3, double par5, float par7)
    122        {
    123            PathEntity var8 = this.getPathToXYZ((double)MathHelper.floor_double(par1), (double)((int)par3), (double)MathHelper.floor_double(par5));
    124            return this.setPath(var8, par7);
    125        }
    126
    127        /**
    128         * Returns the path to the given EntityLiving
    129         */
    130        public PathEntity getPathToEntityLiving(EntityLiving par1EntityLiving)
    131        {
    132            return !this.canNavigate() ? null : this.worldObj.getPathEntityToEntity(this.theEntity, par1EntityLiving, this.pathSearchRange, this.canPassOpenWoodenDoors, this.canPassClosedWoodenDoors, this.avoidsWater, this.canSwim);
    133        }
    134
    135        /**
    136         * Try to find and set a path to EntityLiving. Returns true if successful.
    137         */
    138        public boolean tryMoveToEntityLiving(EntityLiving par1EntityLiving, float par2)
    139        {
    140            PathEntity var3 = this.getPathToEntityLiving(par1EntityLiving);
    141            return var3 != null ? this.setPath(var3, par2) : false;
    142        }
    143
    144        /**
    145         * sets the active path data if path is 100% unique compared to old path, checks to adjust path for sun avoiding
    146         * ents and stores end coords
    147         */
    148        public boolean setPath(PathEntity par1PathEntity, float par2)
    149        {
    150            if (par1PathEntity == null)
    151            {
    152                this.currentPath = null;
    153                return false;
    154            }
    155            else
    156            {
    157                if (!par1PathEntity.isSamePath(this.currentPath))
    158                {
    159                    this.currentPath = par1PathEntity;
    160                }
    161
    162                if (this.noSunPathfind)
    163                {
    164                    this.removeSunnyPath();
    165                }
    166
    167                if (this.currentPath.getCurrentPathLength() == 0)
    168                {
    169                    return false;
    170                }
    171                else
    172                {
    173                    this.speed = par2;
    174                    Vec3 var3 = this.getEntityPosition();
    175                    this.ticksAtLastPos = this.totalTicks;
    176                    this.lastPosCheck.xCoord = var3.xCoord;
    177                    this.lastPosCheck.yCoord = var3.yCoord;
    178                    this.lastPosCheck.zCoord = var3.zCoord;
    179                    return true;
    180                }
    181            }
    182        }
    183
    184        /**
    185         * gets the actively used PathEntity
    186         */
    187        public PathEntity getPath()
    188        {
    189            return this.currentPath;
    190        }
    191
    192        public void onUpdateNavigation()
    193        {
    194            ++this.totalTicks;
    195
    196            if (!this.noPath())
    197            {
    198                if (this.canNavigate())
    199                {
    200                    this.pathFollow();
    201                }
    202
    203                if (!this.noPath())
    204                {
    205                    Vec3 var1 = this.currentPath.getPosition(this.theEntity);
    206
    207                    if (var1 != null)
    208                    {
    209                        this.theEntity.getMoveHelper().setMoveTo(var1.xCoord, var1.yCoord, var1.zCoord, this.speed);
    210                    }
    211                }
    212            }
    213        }
    214
    215        private void pathFollow()
    216        {
    217            Vec3 var1 = this.getEntityPosition();
    218            int var2 = this.currentPath.getCurrentPathLength();
    219
    220            for (int var3 = this.currentPath.getCurrentPathIndex(); var3 < this.currentPath.getCurrentPathLength(); ++var3)
    221            {
    222                if (this.currentPath.getPathPointFromIndex(var3).yCoord != (int)var1.yCoord)
    223                {
    224                    var2 = var3;
    225                    break;
    226                }
    227            }
    228
    229            float var8 = this.theEntity.width * this.theEntity.width;
    230            int var4;
    231
    232            for (var4 = this.currentPath.getCurrentPathIndex(); var4 < var2; ++var4)
    233            {
    234                if (var1.squareDistanceTo(this.currentPath.getVectorFromIndex(this.theEntity, var4)) < (double)var8)
    235                {
    236                    this.currentPath.setCurrentPathIndex(var4 + 1);
    237                }
    238            }
    239
    240            var4 = MathHelper.ceiling_float_int(this.theEntity.width);
    241            int var5 = (int)this.theEntity.height + 1;
    242            int var6 = var4;
    243
    244            for (int var7 = var2 - 1; var7 >= this.currentPath.getCurrentPathIndex(); --var7)
    245            {
    246                if (this.isDirectPathBetweenPoints(var1, this.currentPath.getVectorFromIndex(this.theEntity, var7), var4, var5, var6))
    247                {
    248                    this.currentPath.setCurrentPathIndex(var7);
    249                    break;
    250                }
    251            }
    252
    253            if (this.totalTicks - this.ticksAtLastPos > 100)
    254            {
    255                if (var1.squareDistanceTo(this.lastPosCheck) < 2.25D)
    256                {
    257                    this.clearPathEntity();
    258                }
    259
    260                this.ticksAtLastPos = this.totalTicks;
    261                this.lastPosCheck.xCoord = var1.xCoord;
    262                this.lastPosCheck.yCoord = var1.yCoord;
    263                this.lastPosCheck.zCoord = var1.zCoord;
    264            }
    265        }
    266
    267        /**
    268         * If null path or reached the end
    269         */
    270        public boolean noPath()
    271        {
    272            return this.currentPath == null || this.currentPath.isFinished();
    273        }
    274
    275        /**
    276         * sets active PathEntity to null
    277         */
    278        public void clearPathEntity()
    279        {
    280            this.currentPath = null;
    281        }
    282
    283        private Vec3 getEntityPosition()
    284        {
    285            return this.worldObj.getWorldVec3Pool().getVecFromPool(this.theEntity.posX, (double)this.getPathableYPos(), this.theEntity.posZ);
    286        }
    287
    288        /**
    289         * Gets the safe pathing Y position for the entity depending on if it can path swim or not
    290         */
    291        private int getPathableYPos()
    292        {
    293            if (this.theEntity.isInWater() && this.canSwim)
    294            {
    295                int var1 = (int)this.theEntity.boundingBox.minY;
    296                int var2 = this.worldObj.getBlockId(MathHelper.floor_double(this.theEntity.posX), var1, MathHelper.floor_double(this.theEntity.posZ));
    297                int var3 = 0;
    298
    299                do
    300                {
    301                    if (var2 != Block.waterMoving.blockID && var2 != Block.waterStill.blockID)
    302                    {
    303                        return var1;
    304                    }
    305
    306                    ++var1;
    307                    var2 = this.worldObj.getBlockId(MathHelper.floor_double(this.theEntity.posX), var1, MathHelper.floor_double(this.theEntity.posZ));
    308                    ++var3;
    309                }
    310                while (var3 <= 16);
    311
    312                return (int)this.theEntity.boundingBox.minY;
    313            }
    314            else
    315            {
    316                return (int)(this.theEntity.boundingBox.minY + 0.5D);
    317            }
    318        }
    319
    320        /**
    321         * If on ground or swimming and can swim
    322         */
    323        private boolean canNavigate()
    324        {
    325            return this.theEntity.onGround || this.canSwim && this.isInFluid();
    326        }
    327
    328        /**
    329         * Returns true if the entity is in water or lava, false otherwise
    330         */
    331        private boolean isInFluid()
    332        {
    333            return this.theEntity.isInWater() || this.theEntity.handleLavaMovement();
    334        }
    335
    336        /**
    337         * Trims path data from the end to the first sun covered block
    338         */
    339        private void removeSunnyPath()
    340        {
    341            if (!this.worldObj.canBlockSeeTheSky(MathHelper.floor_double(this.theEntity.posX), (int)(this.theEntity.boundingBox.minY + 0.5D), MathHelper.floor_double(this.theEntity.posZ)))
    342            {
    343                for (int var1 = 0; var1 < this.currentPath.getCurrentPathLength(); ++var1)
    344                {
    345                    PathPoint var2 = this.currentPath.getPathPointFromIndex(var1);
    346
    347                    if (this.worldObj.canBlockSeeTheSky(var2.xCoord, var2.yCoord, var2.zCoord))
    348                    {
    349                        this.currentPath.setCurrentPathLength(var1 - 1);
    350                        return;
    351                    }
    352                }
    353            }
    354        }
    355
    356        /**
    357         * Returns true when an entity of specified size could safely walk in a straight line between the two points. Args:
    358         * pos1, pos2, entityXSize, entityYSize, entityZSize
    359         */
    360        private boolean isDirectPathBetweenPoints(Vec3 par1Vec3, Vec3 par2Vec3, int par3, int par4, int par5)
    361        {
    362            int var6 = MathHelper.floor_double(par1Vec3.xCoord);
    363            int var7 = MathHelper.floor_double(par1Vec3.zCoord);
    364            double var8 = par2Vec3.xCoord - par1Vec3.xCoord;
    365            double var10 = par2Vec3.zCoord - par1Vec3.zCoord;
    366            double var12 = var8 * var8 + var10 * var10;
    367
    368            if (var12 < 1.0E-8D)
    369            {
    370                return false;
    371            }
    372            else
    373            {
    374                double var14 = 1.0D / Math.sqrt(var12);
    375                var8 *= var14;
    376                var10 *= var14;
    377                par3 += 2;
    378                par5 += 2;
    379
    380                if (!this.isSafeToStandAt(var6, (int)par1Vec3.yCoord, var7, par3, par4, par5, par1Vec3, var8, var10))
    381                {
    382                    return false;
    383                }
    384                else
    385                {
    386                    par3 -= 2;
    387                    par5 -= 2;
    388                    double var16 = 1.0D / Math.abs(var8);
    389                    double var18 = 1.0D / Math.abs(var10);
    390                    double var20 = (double)(var6 * 1) - par1Vec3.xCoord;
    391                    double var22 = (double)(var7 * 1) - par1Vec3.zCoord;
    392
    393                    if (var8 >= 0.0D)
    394                    {
    395                        ++var20;
    396                    }
    397
    398                    if (var10 >= 0.0D)
    399                    {
    400                        ++var22;
    401                    }
    402
    403                    var20 /= var8;
    404                    var22 /= var10;
    405                    int var24 = var8 < 0.0D ? -1 : 1;
    406                    int var25 = var10 < 0.0D ? -1 : 1;
    407                    int var26 = MathHelper.floor_double(par2Vec3.xCoord);
    408                    int var27 = MathHelper.floor_double(par2Vec3.zCoord);
    409                    int var28 = var26 - var6;
    410                    int var29 = var27 - var7;
    411
    412                    do
    413                    {
    414                        if (var28 * var24 <= 0 && var29 * var25 <= 0)
    415                        {
    416                            return true;
    417                        }
    418
    419                        if (var20 < var22)
    420                        {
    421                            var20 += var16;
    422                            var6 += var24;
    423                            var28 = var26 - var6;
    424                        }
    425                        else
    426                        {
    427                            var22 += var18;
    428                            var7 += var25;
    429                            var29 = var27 - var7;
    430                        }
    431                    }
    432                    while (this.isSafeToStandAt(var6, (int)par1Vec3.yCoord, var7, par3, par4, par5, par1Vec3, var8, var10));
    433
    434                    return false;
    435                }
    436            }
    437        }
    438
    439        /**
    440         * Returns true when an entity could stand at a position, including solid blocks under the entire entity. Args:
    441         * xOffset, yOffset, zOffset, entityXSize, entityYSize, entityZSize, originPosition, vecX, vecZ
    442         */
    443        private boolean isSafeToStandAt(int par1, int par2, int par3, int par4, int par5, int par6, Vec3 par7Vec3, double par8, double par10)
    444        {
    445            int var12 = par1 - par4 / 2;
    446            int var13 = par3 - par6 / 2;
    447
    448            if (!this.isPositionClear(var12, par2, var13, par4, par5, par6, par7Vec3, par8, par10))
    449            {
    450                return false;
    451            }
    452            else
    453            {
    454                for (int var14 = var12; var14 < var12 + par4; ++var14)
    455                {
    456                    for (int var15 = var13; var15 < var13 + par6; ++var15)
    457                    {
    458                        double var16 = (double)var14 + 0.5D - par7Vec3.xCoord;
    459                        double var18 = (double)var15 + 0.5D - par7Vec3.zCoord;
    460
    461                        if (var16 * par8 + var18 * par10 >= 0.0D)
    462                        {
    463                            int var20 = this.worldObj.getBlockId(var14, par2 - 1, var15);
    464
    465                            if (var20 <= 0)
    466                            {
    467                                return false;
    468                            }
    469
    470                            Material var21 = Block.blocksList[var20].blockMaterial;
    471
    472                            if (var21 == Material.water && !this.theEntity.isInWater())
    473                            {
    474                                return false;
    475                            }
    476
    477                            if (var21 == Material.lava)
    478                            {
    479                                return false;
    480                            }
    481                        }
    482                    }
    483                }
    484
    485                return true;
    486            }
    487        }
    488
    489        /**
    490         * Returns true if an entity does not collide with any solid blocks at the position. Args: xOffset, yOffset,
    491         * zOffset, entityXSize, entityYSize, entityZSize, originPosition, vecX, vecZ
    492         */
    493        private boolean isPositionClear(int par1, int par2, int par3, int par4, int par5, int par6, Vec3 par7Vec3, double par8, double par10)
    494        {
    495            for (int var12 = par1; var12 < par1 + par4; ++var12)
    496            {
    497                for (int var13 = par2; var13 < par2 + par5; ++var13)
    498                {
    499                    for (int var14 = par3; var14 < par3 + par6; ++var14)
    500                    {
    501                        double var15 = (double)var12 + 0.5D - par7Vec3.xCoord;
    502                        double var17 = (double)var14 + 0.5D - par7Vec3.zCoord;
    503
    504                        if (var15 * par8 + var17 * par10 >= 0.0D)
    505                        {
    506                            int var19 = this.worldObj.getBlockId(var12, var13, var14);
    507
    508                            if (var19 > 0 && !Block.blocksList[var19].getBlocksMovement(this.worldObj, var12, var13, var14))
    509                            {
    510                                return false;
    511                            }
    512                        }
    513                    }
    514                }
    515            }
    516
    517            return true;
    518        }
    519    }