    001package net.minecraft.pathfinding;
    002
    003import net.minecraft.block.Block;
    004import net.minecraft.block.material.Material;
    005import net.minecraft.entity.Entity;
    006import net.minecraft.util.IntHashMap;
    007import net.minecraft.util.MathHelper;
    008import net.minecraft.world.IBlockAccess;
    009
    010public class PathFinder
    011{
    012    /** Used to find obstacles */
    013    private IBlockAccess worldMap;
    014
    015    /** The path being generated */
    016    private Path path = new Path();
    017
    018    /** The points in the path */
    019    private IntHashMap pointMap = new IntHashMap();
    020
    021    /** Selection of path points to add to the path */
    022    private PathPoint[] pathOptions = new PathPoint[32];
    023
    024    /** should the PathFinder go through wodden door blocks */
    025    private boolean isWoddenDoorAllowed;
    026
    027    /**
    028     * should the PathFinder disregard BlockMovement type materials in its path
    029     */
    030    private boolean isMovementBlockAllowed;
    031    private boolean isPathingInWater;
    032
    033    /** tells the FathFinder to not stop pathing underwater */
    034    private boolean canEntityDrown;
    035
    036    public PathFinder(IBlockAccess par1IBlockAccess, boolean par2, boolean par3, boolean par4, boolean par5)
    037    {
    038        this.worldMap = par1IBlockAccess;
    039        this.isWoddenDoorAllowed = par2;
    040        this.isMovementBlockAllowed = par3;
    041        this.isPathingInWater = par4;
    042        this.canEntityDrown = par5;
    043    }
    044
    045    /**
    046     * Creates a path from one entity to another within a minimum distance
    047     */
    048    public PathEntity createEntityPathTo(Entity par1Entity, Entity par2Entity, float par3)
    049    {
    050        return this.createEntityPathTo(par1Entity, par2Entity.posX, par2Entity.boundingBox.minY, par2Entity.posZ, par3);
    051    }
    052
    053    /**
    054     * Creates a path from an entity to a specified location within a minimum distance
    055     */
    056    public PathEntity createEntityPathTo(Entity par1Entity, int par2, int par3, int par4, float par5)
    057    {
    058        return this.createEntityPathTo(par1Entity, (double)((float)par2 + 0.5F), (double)((float)par3 + 0.5F), (double)((float)par4 + 0.5F), par5);
    059    }
    060
    061    /**
    062     * Internal implementation of creating a path from an entity to a point
    063     */
    064    private PathEntity createEntityPathTo(Entity par1Entity, double par2, double par4, double par6, float par8)
    065    {
    066        this.path.clearPath();
    067        this.pointMap.clearMap();
    068        boolean flag = this.isPathingInWater;
    069        int i = MathHelper.floor_double(par1Entity.boundingBox.minY + 0.5D);
    070
    071        if (this.canEntityDrown && par1Entity.isInWater())
    072        {
    073            i = (int)par1Entity.boundingBox.minY;
    074
    075            for (int j = this.worldMap.getBlockId(MathHelper.floor_double(par1Entity.posX), i, MathHelper.floor_double(par1Entity.posZ)); j == Block.waterMoving.blockID || j == Block.waterStill.blockID; j = this.worldMap.getBlockId(MathHelper.floor_double(par1Entity.posX), i, MathHelper.floor_double(par1Entity.posZ)))
    076            {
    077                ++i;
    078            }
    079
    080            flag = this.isPathingInWater;
    081            this.isPathingInWater = false;
    082        }
    083        else
    084        {
    085            i = MathHelper.floor_double(par1Entity.boundingBox.minY + 0.5D);
    086        }
    087
    088        PathPoint pathpoint = this.openPoint(MathHelper.floor_double(par1Entity.boundingBox.minX), i, MathHelper.floor_double(par1Entity.boundingBox.minZ));
    089        PathPoint pathpoint1 = this.openPoint(MathHelper.floor_double(par2 - (double)(par1Entity.width / 2.0F)), MathHelper.floor_double(par4), MathHelper.floor_double(par6 - (double)(par1Entity.width / 2.0F)));
    090        PathPoint pathpoint2 = new PathPoint(MathHelper.floor_float(par1Entity.width + 1.0F), MathHelper.floor_float(par1Entity.height + 1.0F), MathHelper.floor_float(par1Entity.width + 1.0F));
    091        PathEntity pathentity = this.addToPath(par1Entity, pathpoint, pathpoint1, pathpoint2, par8);
    092        this.isPathingInWater = flag;
    093        return pathentity;
    094    }
    095
    096    /**
    097     * Adds a path from start to end and returns the whole path (args: unused, start, end, unused, maxDistance)
    098     */
    099    private PathEntity addToPath(Entity par1Entity, PathPoint par2PathPoint, PathPoint par3PathPoint, PathPoint par4PathPoint, float par5)
    100    {
    101        par2PathPoint.totalPathDistance = 0.0F;
    102        par2PathPoint.distanceToNext = par2PathPoint.func_75832_b(par3PathPoint);
    103        par2PathPoint.distanceToTarget = par2PathPoint.distanceToNext;
    104        this.path.clearPath();
    105        this.path.addPoint(par2PathPoint);
    106        PathPoint pathpoint3 = par2PathPoint;
    107
    108        while (!this.path.isPathEmpty())
    109        {
    110            PathPoint pathpoint4 = this.path.dequeue();
    111
    112            if (pathpoint4.equals(par3PathPoint))
    113            {
    114                return this.createEntityPath(par2PathPoint, par3PathPoint);
    115            }
    116
    117            if (pathpoint4.func_75832_b(par3PathPoint) < pathpoint3.func_75832_b(par3PathPoint))
    118            {
    119                pathpoint3 = pathpoint4;
    120            }
    121
    122            pathpoint4.isFirst = true;
    123            int i = this.findPathOptions(par1Entity, pathpoint4, par4PathPoint, par3PathPoint, par5);
    124
    125            for (int j = 0; j < i; ++j)
    126            {
    127                PathPoint pathpoint5 = this.pathOptions[j];
    128                float f1 = pathpoint4.totalPathDistance + pathpoint4.func_75832_b(pathpoint5);
    129
    130                if (!pathpoint5.isAssigned() || f1 < pathpoint5.totalPathDistance)
    131                {
    132                    pathpoint5.previous = pathpoint4;
    133                    pathpoint5.totalPathDistance = f1;
    134                    pathpoint5.distanceToNext = pathpoint5.func_75832_b(par3PathPoint);
    135
    136                    if (pathpoint5.isAssigned())
    137                    {
    138                        this.path.changeDistance(pathpoint5, pathpoint5.totalPathDistance + pathpoint5.distanceToNext);
    139                    }
    140                    else
    141                    {
    142                        pathpoint5.distanceToTarget = pathpoint5.totalPathDistance + pathpoint5.distanceToNext;
    143                        this.path.addPoint(pathpoint5);
    144                    }
    145                }
    146            }
    147        }
    148
    149        if (pathpoint3 == par2PathPoint)
    150        {
    151            return null;
    152        }
    153        else
    154        {
    155            return this.createEntityPath(par2PathPoint, pathpoint3);
    156        }
    157    }
    158
    159    /**
    160     * populates pathOptions with available points and returns the number of options found (args: unused1, currentPoint,
    161     * unused2, targetPoint, maxDistance)
    162     */
    163    private int findPathOptions(Entity par1Entity, PathPoint par2PathPoint, PathPoint par3PathPoint, PathPoint par4PathPoint, float par5)
    164    {
    165        int i = 0;
    166        byte b0 = 0;
    167
    168        if (this.getVerticalOffset(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord + 1, par2PathPoint.zCoord, par3PathPoint) == 1)
    169        {
    170            b0 = 1;
    171        }
    172
    173        PathPoint pathpoint3 = this.getSafePoint(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord, par2PathPoint.zCoord + 1, par3PathPoint, b0);
    174        PathPoint pathpoint4 = this.getSafePoint(par1Entity, par2PathPoint.xCoord - 1, par2PathPoint.yCoord, par2PathPoint.zCoord, par3PathPoint, b0);
    175        PathPoint pathpoint5 = this.getSafePoint(par1Entity, par2PathPoint.xCoord + 1, par2PathPoint.yCoord, par2PathPoint.zCoord, par3PathPoint, b0);
    176        PathPoint pathpoint6 = this.getSafePoint(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord, par2PathPoint.zCoord - 1, par3PathPoint, b0);
    177
    178        if (pathpoint3 != null && !pathpoint3.isFirst && pathpoint3.distanceTo(par4PathPoint) < par5)
    179        {
    180            this.pathOptions[i++] = pathpoint3;
    181        }
    182
    183        if (pathpoint4 != null && !pathpoint4.isFirst && pathpoint4.distanceTo(par4PathPoint) < par5)
    184        {
    185            this.pathOptions[i++] = pathpoint4;
    186        }
    187
    188        if (pathpoint5 != null && !pathpoint5.isFirst && pathpoint5.distanceTo(par4PathPoint) < par5)
    189        {
    190            this.pathOptions[i++] = pathpoint5;
    191        }
    192
    193        if (pathpoint6 != null && !pathpoint6.isFirst && pathpoint6.distanceTo(par4PathPoint) < par5)
    194        {
    195            this.pathOptions[i++] = pathpoint6;
    196        }
    197
    198        return i;
    199    }
    200
    201    /**
    202     * Returns a point that the entity can safely move to
    203     */
    204    private PathPoint getSafePoint(Entity par1Entity, int par2, int par3, int par4, PathPoint par5PathPoint, int par6)
    205    {
    206        PathPoint pathpoint1 = null;
    207        int i1 = this.getVerticalOffset(par1Entity, par2, par3, par4, par5PathPoint);
    208
    209        if (i1 == 2)
    210        {
    211            return this.openPoint(par2, par3, par4);
    212        }
    213        else
    214        {
    215            if (i1 == 1)
    216            {
    217                pathpoint1 = this.openPoint(par2, par3, par4);
    218            }
    219
    220            if (pathpoint1 == null && par6 > 0 && i1 != -3 && i1 != -4 && this.getVerticalOffset(par1Entity, par2, par3 + par6, par4, par5PathPoint) == 1)
    221            {
    222                pathpoint1 = this.openPoint(par2, par3 + par6, par4);
    223                par3 += par6;
    224            }
    225
    226            if (pathpoint1 != null)
    227            {
    228                int j1 = 0;
    229                int k1 = 0;
    230
    231                while (par3 > 0)
    232                {
    233                    k1 = this.getVerticalOffset(par1Entity, par2, par3 - 1, par4, par5PathPoint);
    234
    235                    if (this.isPathingInWater && k1 == -1)
    236                    {
    237                        return null;
    238                    }
    239
    240                    if (k1 != 1)
    241                    {
    242                        break;
    243                    }
    244
    245                    if (j1++ >= par1Entity.func_82143_as())
    246                    {
    247                        return null;
    248                    }
    249
    250                    --par3;
    251
    252                    if (par3 > 0)
    253                    {
    254                        pathpoint1 = this.openPoint(par2, par3, par4);
    255                    }
    256                }
    257
    258                if (k1 == -2)
    259                {
    260                    return null;
    261                }
    262            }
    263
    264            return pathpoint1;
    265        }
    266    }
    267
    268    /**
    269     * Returns a mapped point or creates and adds one
    270     */
    271    private final PathPoint openPoint(int par1, int par2, int par3)
    272    {
    273        int l = PathPoint.makeHash(par1, par2, par3);
    274        PathPoint pathpoint = (PathPoint)this.pointMap.lookup(l);
    275
    276        if (pathpoint == null)
    277        {
    278            pathpoint = new PathPoint(par1, par2, par3);
    279            this.pointMap.addKey(l, pathpoint);
    280        }
    281
    282        return pathpoint;
    283    }
    284
    285    /**
    286     * Checks if an entity collides with blocks at a position. Returns 1 if clear, 0 for colliding with any solid block,
    287     * -1 for water(if avoiding water) but otherwise clear, -2 for lava, -3 for fence, -4 for closed trapdoor, 2 if
    288     * otherwise clear except for open trapdoor or water(if not avoiding)
    289     */
    290    public int getVerticalOffset(Entity par1Entity, int par2, int par3, int par4, PathPoint par5PathPoint)
    291    {
    292        return func_82565_a(par1Entity, par2, par3, par4, par5PathPoint, this.isPathingInWater, this.isMovementBlockAllowed, this.isWoddenDoorAllowed);
    293    }
    294
    295    public static int func_82565_a(Entity par0Entity, int par1, int par2, int par3, PathPoint par4PathPoint, boolean par5, boolean par6, boolean par7)
    296    {
    297        boolean flag3 = false;
    298
    299        for (int l = par1; l < par1 + par4PathPoint.xCoord; ++l)
    300        {
    301            for (int i1 = par2; i1 < par2 + par4PathPoint.yCoord; ++i1)
    302            {
    303                for (int j1 = par3; j1 < par3 + par4PathPoint.zCoord; ++j1)
    304                {
    305                    int k1 = par0Entity.worldObj.getBlockId(l, i1, j1);
    306
    307                    if (k1 > 0)
    308                    {
    309                        if (k1 == Block.trapdoor.blockID)
    310                        {
    311                            flag3 = true;
    312                        }
    313                        else if (k1 != Block.waterMoving.blockID && k1 != Block.waterStill.blockID)
    314                        {
    315                            if (!par7 && k1 == Block.doorWood.blockID)
    316                            {
    317                                return 0;
    318                            }
    319                        }
    320                        else
    321                        {
    322                            if (par5)
    323                            {
    324                                return -1;
    325                            }
    326
    327                            flag3 = true;
    328                        }
    329
    330                        Block block = Block.blocksList[k1];
    331                        int l1 = block.getRenderType();
    332
    333                        if (par0Entity.worldObj.blockGetRenderType(l, i1, j1) == 9)
    334                        {
    335                            int i2 = MathHelper.floor_double(par0Entity.posX);
    336                            int j2 = MathHelper.floor_double(par0Entity.posY);
    337                            int k2 = MathHelper.floor_double(par0Entity.posZ);
    338
    339                            if (par0Entity.worldObj.blockGetRenderType(i2, j2, k2) != 9 && par0Entity.worldObj.blockGetRenderType(i2, j2 - 1, k2) != 9)
    340                            {
    341                                return -3;
    342                            }
    343                        }
    344                        else if (!block.getBlocksMovement(par0Entity.worldObj, l, i1, j1) && (!par6 || k1 != Block.doorWood.blockID))
    345                        {
    346                            if (l1 == 11 || k1 == Block.fenceGate.blockID || l1 == 32)
    347                            {
    348                                return -3;
    349                            }
    350
    351                            if (k1 == Block.trapdoor.blockID)
    352                            {
    353                                return -4;
    354                            }
    355
    356                            Material material = block.blockMaterial;
    357
    358                            if (material != Material.lava)
    359                            {
    360                                return 0;
    361                            }
    362
    363                            if (!par0Entity.handleLavaMovement())
    364                            {
    365                                return -2;
    366                            }
    367                        }
    368                    }
    369                }
    370            }
    371        }
    372
    373        return flag3 ? 2 : 1;
    374    }
    375
    376    /**
    377     * Returns a new PathEntity for a given start and end point
    378     */
    379    private PathEntity createEntityPath(PathPoint par1PathPoint, PathPoint par2PathPoint)
    380    {
    381        int i = 1;
    382        PathPoint pathpoint2;
    383
    384        for (pathpoint2 = par2PathPoint; pathpoint2.previous != null; pathpoint2 = pathpoint2.previous)
    385        {
    386            ++i;
    387        }
    388
    389        PathPoint[] apathpoint = new PathPoint[i];
    390        pathpoint2 = par2PathPoint;
    391        --i;
    392
    393        for (apathpoint[i] = par2PathPoint; pathpoint2.previous != null; apathpoint[i] = pathpoint2)
    394        {
    395            pathpoint2 = pathpoint2.previous;
    396            --i;
    397        }
    398
    399        return new PathEntity(apathpoint);
    400    }
    401}