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