    001package net.minecraft.pathfinding;
    002
    003import net.minecraft.block.Block;
    004import net.minecraft.block.material.Material;
    005import net.minecraft.entity.EntityLiving;
    006import net.minecraft.util.MathHelper;
    007import net.minecraft.util.Vec3;
    008import net.minecraft.world.World;
    009
    010public 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}