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