    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                        ++var9;
    239
    240                        if (var9 >= 4)
    241                        {
    242                            return null;
    243                        }
    244
    245                        --par3;
    246
    247                        if (par3 > 0)
    248                        {
    249                            var7 = this.openPoint(par2, par3, par4);
    250                        }
    251                    }
    252
    253                    if (var10 == -2)
    254                    {
    255                        return null;
    256                    }
    257                }
    258
    259                return var7;
    260            }
    261        }
    262
    263        /**
    264         * Returns a mapped point or creates and adds one
    265         */
    266        private final PathPoint openPoint(int par1, int par2, int par3)
    267        {
    268            int var4 = PathPoint.makeHash(par1, par2, par3);
    269            PathPoint var5 = (PathPoint)this.pointMap.lookup(var4);
    270
    271            if (var5 == null)
    272            {
    273                var5 = new PathPoint(par1, par2, par3);
    274                this.pointMap.addKey(var4, var5);
    275            }
    276
    277            return var5;
    278        }
    279
    280        /**
    281         * Checks if an entity collides with blocks at a position. Returns 1 if clear, 0 for colliding with any solid block,
    282         * -1 for water(if avoiding water) but otherwise clear, -2 for lava, -3 for fence, -4 for closed trapdoor, 2 if
    283         * otherwise clear except for open trapdoor or water(if not avoiding)
    284         */
    285        private int getVerticalOffset(Entity par1Entity, int par2, int par3, int par4, PathPoint par5PathPoint)
    286        {
    287            boolean var6 = false;
    288
    289            for (int var7 = par2; var7 < par2 + par5PathPoint.xCoord; ++var7)
    290            {
    291                for (int var8 = par3; var8 < par3 + par5PathPoint.yCoord; ++var8)
    292                {
    293                    for (int var9 = par4; var9 < par4 + par5PathPoint.zCoord; ++var9)
    294                    {
    295                        int var10 = this.worldMap.getBlockId(var7, var8, var9);
    296
    297                        if (var10 > 0)
    298                        {
    299                            if (var10 == Block.trapdoor.blockID)
    300                            {
    301                                var6 = true;
    302                            }
    303                            else if (var10 != Block.waterMoving.blockID && var10 != Block.waterStill.blockID)
    304                            {
    305                                if (!this.isWoddenDoorAllowed && var10 == Block.doorWood.blockID)
    306                                {
    307                                    return 0;
    308                                }
    309                            }
    310                            else
    311                            {
    312                                if (this.isPathingInWater)
    313                                {
    314                                    return -1;
    315                                }
    316
    317                                var6 = true;
    318                            }
    319
    320                            Block var11 = Block.blocksList[var10];
    321
    322                            if (!var11.getBlocksMovement(this.worldMap, var7, var8, var9) && (!this.isMovementBlockAllowed || var10 != Block.doorWood.blockID))
    323                            {
    324                                if (var10 == Block.fence.blockID || var10 == Block.fenceGate.blockID)
    325                                {
    326                                    return -3;
    327                                }
    328
    329                                if (var10 == Block.trapdoor.blockID)
    330                                {
    331                                    return -4;
    332                                }
    333
    334                                Material var12 = var11.blockMaterial;
    335
    336                                if (var12 != Material.lava)
    337                                {
    338                                    return 0;
    339                                }
    340
    341                                if (!par1Entity.handleLavaMovement())
    342                                {
    343                                    return -2;
    344                                }
    345                            }
    346                        }
    347                    }
    348                }
    349            }
    350
    351            return var6 ? 2 : 1;
    352        }
    353
    354        /**
    355         * Returns a new PathEntity for a given start and end point
    356         */
    357        private PathEntity createEntityPath(PathPoint par1PathPoint, PathPoint par2PathPoint)
    358        {
    359            int var3 = 1;
    360            PathPoint var4;
    361
    362            for (var4 = par2PathPoint; var4.previous != null; var4 = var4.previous)
    363            {
    364                ++var3;
    365            }
    366
    367            PathPoint[] var5 = new PathPoint[var3];
    368            var4 = par2PathPoint;
    369            --var3;
    370
    371            for (var5[var3] = par2PathPoint; var4.previous != null; var5[var3] = var4)
    372            {
    373                var4 = var4.previous;
    374                --var3;
    375            }
    376
    377            return new PathEntity(var5);
    378        }
    379    }