    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 pathentity = this.getPathToXYZ((double)MathHelper.floor_double(par1), (double)((int)par3), (double)MathHelper.floor_double(par5));
    131        return this.setPath(pathentity, 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 pathentity = this.getPathToEntityLiving(par1EntityLiving);
    148        return pathentity != null ? this.setPath(pathentity, 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 vec3 = this.getEntityPosition();
    182                this.ticksAtLastPos = this.totalTicks;
    183                this.lastPosCheck.xCoord = vec3.xCoord;
    184                this.lastPosCheck.yCoord = vec3.yCoord;
    185                this.lastPosCheck.zCoord = vec3.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 vec3 = this.currentPath.getPosition(this.theEntity);
    213
    214                if (vec3 != null)
    215                {
    216                    this.theEntity.getMoveHelper().setMoveTo(vec3.xCoord, vec3.yCoord, vec3.zCoord, this.speed);
    217                }
    218            }
    219        }
    220    }
    221
    222    private void pathFollow()
    223    {
    224        Vec3 vec3 = this.getEntityPosition();
    225        int i = this.currentPath.getCurrentPathLength();
    226
    227        for (int j = this.currentPath.getCurrentPathIndex(); j < this.currentPath.getCurrentPathLength(); ++j)
    228        {
    229            if (this.currentPath.getPathPointFromIndex(j).yCoord != (int)vec3.yCoord)
    230            {
    231                i = j;
    232                break;
    233            }
    234        }
    235
    236        float f = this.theEntity.width * this.theEntity.width;
    237        int k;
    238
    239        for (k = this.currentPath.getCurrentPathIndex(); k < i; ++k)
    240        {
    241            if (vec3.squareDistanceTo(this.currentPath.getVectorFromIndex(this.theEntity, k)) < (double)f)
    242            {
    243                this.currentPath.setCurrentPathIndex(k + 1);
    244            }
    245        }
    246
    247        k = MathHelper.ceiling_float_int(this.theEntity.width);
    248        int l = (int)this.theEntity.height + 1;
    249        int i1 = k;
    250
    251        for (int j1 = i - 1; j1 >= this.currentPath.getCurrentPathIndex(); --j1)
    252        {
    253            if (this.isDirectPathBetweenPoints(vec3, this.currentPath.getVectorFromIndex(this.theEntity, j1), k, l, i1))
    254            {
    255                this.currentPath.setCurrentPathIndex(j1);
    256                break;
    257            }
    258        }
    259
    260        if (this.totalTicks - this.ticksAtLastPos > 100)
    261        {
    262            if (vec3.squareDistanceTo(this.lastPosCheck) < 2.25D)
    263            {
    264                this.clearPathEntity();
    265            }
    266
    267            this.ticksAtLastPos = this.totalTicks;
    268            this.lastPosCheck.xCoord = vec3.xCoord;
    269            this.lastPosCheck.yCoord = vec3.yCoord;
    270            this.lastPosCheck.zCoord = vec3.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 i = (int)this.theEntity.boundingBox.minY;
    303            int j = this.worldObj.getBlockId(MathHelper.floor_double(this.theEntity.posX), i, MathHelper.floor_double(this.theEntity.posZ));
    304            int k = 0;
    305
    306            do
    307            {
    308                if (j != Block.waterMoving.blockID && j != Block.waterStill.blockID)
    309                {
    310                    return i;
    311                }
    312
    313                ++i;
    314                j = this.worldObj.getBlockId(MathHelper.floor_double(this.theEntity.posX), i, MathHelper.floor_double(this.theEntity.posZ));
    315                ++k;
    316            }
    317            while (k <= 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 i = 0; i < this.currentPath.getCurrentPathLength(); ++i)
    351            {
    352                PathPoint pathpoint = this.currentPath.getPathPointFromIndex(i);
    353
    354                if (this.worldObj.canBlockSeeTheSky(pathpoint.xCoord, pathpoint.yCoord, pathpoint.zCoord))
    355                {
    356                    this.currentPath.setCurrentPathLength(i - 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 l = MathHelper.floor_double(par1Vec3.xCoord);
    370        int i1 = MathHelper.floor_double(par1Vec3.zCoord);
    371        double d0 = par2Vec3.xCoord - par1Vec3.xCoord;
    372        double d1 = par2Vec3.zCoord - par1Vec3.zCoord;
    373        double d2 = d0 * d0 + d1 * d1;
    374
    375        if (d2 < 1.0E-8D)
    376        {
    377            return false;
    378        }
    379        else
    380        {
    381            double d3 = 1.0D / Math.sqrt(d2);
    382            d0 *= d3;
    383            d1 *= d3;
    384            par3 += 2;
    385            par5 += 2;
    386
    387            if (!this.isSafeToStandAt(l, (int)par1Vec3.yCoord, i1, par3, par4, par5, par1Vec3, d0, d1))
    388            {
    389                return false;
    390            }
    391            else
    392            {
    393                par3 -= 2;
    394                par5 -= 2;
    395                double d4 = 1.0D / Math.abs(d0);
    396                double d5 = 1.0D / Math.abs(d1);
    397                double d6 = (double)(l * 1) - par1Vec3.xCoord;
    398                double d7 = (double)(i1 * 1) - par1Vec3.zCoord;
    399
    400                if (d0 >= 0.0D)
    401                {
    402                    ++d6;
    403                }
    404
    405                if (d1 >= 0.0D)
    406                {
    407                    ++d7;
    408                }
    409
    410                d6 /= d0;
    411                d7 /= d1;
    412                int j1 = d0 < 0.0D ? -1 : 1;
    413                int k1 = d1 < 0.0D ? -1 : 1;
    414                int l1 = MathHelper.floor_double(par2Vec3.xCoord);
    415                int i2 = MathHelper.floor_double(par2Vec3.zCoord);
    416                int j2 = l1 - l;
    417                int k2 = i2 - i1;
    418
    419                do
    420                {
    421                    if (j2 * j1 <= 0 && k2 * k1 <= 0)
    422                    {
    423                        return true;
    424                    }
    425
    426                    if (d6 < d7)
    427                    {
    428                        d6 += d4;
    429                        l += j1;
    430                        j2 = l1 - l;
    431                    }
    432                    else
    433                    {
    434                        d7 += d5;
    435                        i1 += k1;
    436                        k2 = i2 - i1;
    437                    }
    438                }
    439                while (this.isSafeToStandAt(l, (int)par1Vec3.yCoord, i1, par3, par4, par5, par1Vec3, d0, d1));
    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 k1 = par1 - par4 / 2;
    453        int l1 = par3 - par6 / 2;
    454
    455        if (!this.isPositionClear(k1, par2, l1, par4, par5, par6, par7Vec3, par8, par10))
    456        {
    457            return false;
    458        }
    459        else
    460        {
    461            for (int i2 = k1; i2 < k1 + par4; ++i2)
    462            {
    463                for (int j2 = l1; j2 < l1 + par6; ++j2)
    464                {
    465                    double d2 = (double)i2 + 0.5D - par7Vec3.xCoord;
    466                    double d3 = (double)j2 + 0.5D - par7Vec3.zCoord;
    467
    468                    if (d2 * par8 + d3 * par10 >= 0.0D)
    469                    {
    470                        int k2 = this.worldObj.getBlockId(i2, par2 - 1, j2);
    471
    472                        if (k2 <= 0)
    473                        {
    474                            return false;
    475                        }
    476
    477                        Material material = Block.blocksList[k2].blockMaterial;
    478
    479                        if (material == Material.water && !this.theEntity.isInWater())
    480                        {
    481                            return false;
    482                        }
    483
    484                        if (material == 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 k1 = par1; k1 < par1 + par4; ++k1)
    503        {
    504            for (int l1 = par2; l1 < par2 + par5; ++l1)
    505            {
    506                for (int i2 = par3; i2 < par3 + par6; ++i2)
    507                {
    508                    double d2 = (double)k1 + 0.5D - par7Vec3.xCoord;
    509                    double d3 = (double)i2 + 0.5D - par7Vec3.zCoord;
    510
    511                    if (d2 * par8 + d3 * par10 >= 0.0D)
    512                    {
    513                        int j2 = this.worldObj.getBlockId(k1, l1, i2);
    514
    515                        if (j2 > 0 && !Block.blocksList[j2].getBlocksMovement(this.worldObj, k1, l1, i2))
    516                        {
    517                            return false;
    518                        }
    519                    }
    520                }
    521            }
    522        }
    523
    524        return true;
    525    }
    526}