    001    package net.minecraft.pathfinding;
    002
    003    import net.minecraft.block.Block;
    004    import net.minecraft.block.material.Material;
    005    import net.minecraft.entity.Entity;
    006    import net.minecraft.util.IntHashMap;
    007    import net.minecraft.util.MathHelper;
    008    import net.minecraft.world.IBlockAccess;
    009
    010    public 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 var9 = this.isPathingInWater;
    069            int var10 = MathHelper.floor_double(par1Entity.boundingBox.minY + 0.5D);
    070
    071            if (this.canEntityDrown && par1Entity.isInWater())
    072            {
    073                var10 = (int)par1Entity.boundingBox.minY;
    074
    075                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)))
    076                {
    077                    ++var10;
    078                }
    079
    080                var9 = this.isPathingInWater;
    081                this.isPathingInWater = false;
    082            }
    083            else
    084            {
    085                var10 = MathHelper.floor_double(par1Entity.boundingBox.minY + 0.5D);
    086            }
    087
    088            PathPoint var15 = this.openPoint(MathHelper.floor_double(par1Entity.boundingBox.minX), var10, MathHelper.floor_double(par1Entity.boundingBox.minZ));
    089            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)));
    090            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));
    091            PathEntity var14 = this.addToPath(par1Entity, var15, var12, var13, par8);
    092            this.isPathingInWater = var9;
    093            return var14;
    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 var6 = par2PathPoint;
    107
    108            while (!this.path.isPathEmpty())
    109            {
    110                PathPoint var7 = this.path.dequeue();
    111
    112                if (var7.equals(par3PathPoint))
    113                {
    114                    return this.createEntityPath(par2PathPoint, par3PathPoint);
    115                }
    116
    117                if (var7.func_75832_b(par3PathPoint) < var6.func_75832_b(par3PathPoint))
    118                {
    119                    var6 = var7;
    120                }
    121
    122                var7.isFirst = true;
    123                int var8 = this.findPathOptions(par1Entity, var7, par4PathPoint, par3PathPoint, par5);
    124
    125                for (int var9 = 0; var9 < var8; ++var9)
    126                {
    127                    PathPoint var10 = this.pathOptions[var9];
    128                    float var11 = var7.totalPathDistance + var7.func_75832_b(var10);
    129
    130                    if (!var10.isAssigned() || var11 < var10.totalPathDistance)
    131                    {
    132                        var10.previous = var7;
    133                        var10.totalPathDistance = var11;
    134                        var10.distanceToNext = var10.func_75832_b(par3PathPoint);
    135
    136                        if (var10.isAssigned())
    137                        {
    138                            this.path.changeDistance(var10, var10.totalPathDistance + var10.distanceToNext);
    139                        }
    140                        else
    141                        {
    142                            var10.distanceToTarget = var10.totalPathDistance + var10.distanceToNext;
    143                            this.path.addPoint(var10);
    144                        }
    145                    }
    146                }
    147            }
    148
    149            if (var6 == par2PathPoint)
    150            {
    151                return null;
    152            }
    153            else
    154            {
    155                return this.createEntityPath(par2PathPoint, var6);
    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 var6 = 0;
    166            byte var7 = 0;
    167
    168            if (this.getVerticalOffset(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord + 1, par2PathPoint.zCoord, par3PathPoint) == 1)
    169            {
    170                var7 = 1;
    171            }
    172
    173            PathPoint var8 = this.getSafePoint(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord, par2PathPoint.zCoord + 1, par3PathPoint, var7);
    174            PathPoint var9 = this.getSafePoint(par1Entity, par2PathPoint.xCoord - 1, par2PathPoint.yCoord, par2PathPoint.zCoord, par3PathPoint, var7);
    175            PathPoint var10 = this.getSafePoint(par1Entity, par2PathPoint.xCoord + 1, par2PathPoint.yCoord, par2PathPoint.zCoord, par3PathPoint, var7);
    176            PathPoint var11 = this.getSafePoint(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord, par2PathPoint.zCoord - 1, par3PathPoint, var7);
    177
    178            if (var8 != null && !var8.isFirst && var8.distanceTo(par4PathPoint) < par5)
    179            {
    180                this.pathOptions[var6++] = var8;
    181            }
    182
    183            if (var9 != null && !var9.isFirst && var9.distanceTo(par4PathPoint) < par5)
    184            {
    185                this.pathOptions[var6++] = var9;
    186            }
    187
    188            if (var10 != null && !var10.isFirst && var10.distanceTo(par4PathPoint) < par5)
    189            {
    190                this.pathOptions[var6++] = var10;
    191            }
    192
    193            if (var11 != null && !var11.isFirst && var11.distanceTo(par4PathPoint) < par5)
    194            {
    195                this.pathOptions[var6++] = var11;
    196            }
    197
    198            return var6;
    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 var7 = null;
    207            int var8 = this.getVerticalOffset(par1Entity, par2, par3, par4, par5PathPoint);
    208
    209            if (var8 == 2)
    210            {
    211                return this.openPoint(par2, par3, par4);
    212            }
    213            else
    214            {
    215                if (var8 == 1)
    216                {
    217                    var7 = this.openPoint(par2, par3, par4);
    218                }
    219
    220                if (var7 == null && par6 > 0 && var8 != -3 && var8 != -4 && this.getVerticalOffset(par1Entity, par2, par3 + par6, par4, par5PathPoint) == 1)
    221                {
    222                    var7 = this.openPoint(par2, par3 + par6, par4);
    223                    par3 += par6;
    224                }
    225
    226                if (var7 != null)
    227                {
    228                    int var9 = 0;
    229                    int var10 = 0;
    230
    231                    while (par3 > 0)
    232                    {
    233                        var10 = this.getVerticalOffset(par1Entity, par2, par3 - 1, par4, par5PathPoint);
    234
    235                        if (this.isPathingInWater && var10 == -1)
    236                        {
    237                            return null;
    238                        }
    239
    240                        if (var10 != 1)
    241                        {
    242                            break;
    243                        }
    244
    245                        if (var9++ >= par1Entity.func_82143_as())
    246                        {
    247                            return null;
    248                        }
    249
    250                        --par3;
    251
    252                        if (par3 > 0)
    253                        {
    254                            var7 = this.openPoint(par2, par3, par4);
    255                        }
    256                    }
    257
    258                    if (var10 == -2)
    259                    {
    260                        return null;
    261                    }
    262                }
    263
    264                return var7;
    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 var4 = PathPoint.makeHash(par1, par2, par3);
    274            PathPoint var5 = (PathPoint)this.pointMap.lookup(var4);
    275
    276            if (var5 == null)
    277            {
    278                var5 = new PathPoint(par1, par2, par3);
    279                this.pointMap.addKey(var4, var5);
    280            }
    281
    282            return var5;
    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 var8 = false;
    298
    299            for (int var9 = par1; var9 < par1 + par4PathPoint.xCoord; ++var9)
    300            {
    301                for (int var10 = par2; var10 < par2 + par4PathPoint.yCoord; ++var10)
    302                {
    303                    for (int var11 = par3; var11 < par3 + par4PathPoint.zCoord; ++var11)
    304                    {
    305                        int var12 = par0Entity.worldObj.getBlockId(var9, var10, var11);
    306
    307                        if (var12 > 0)
    308                        {
    309                            if (var12 == Block.trapdoor.blockID)
    310                            {
    311                                var8 = true;
    312                            }
    313                            else if (var12 != Block.waterMoving.blockID && var12 != Block.waterStill.blockID)
    314                            {
    315                                if (!par7 && var12 == Block.doorWood.blockID)
    316                                {
    317                                    return 0;
    318                                }
    319                            }
    320                            else
    321                            {
    322                                if (par5)
    323                                {
    324                                    return -1;
    325                                }
    326
    327                                var8 = true;
    328                            }
    329
    330                            Block var13 = Block.blocksList[var12];
    331
    332                            if (!var13.getBlocksMovement(par0Entity.worldObj, var9, var10, var11) && (!par6 || var12 != Block.doorWood.blockID))
    333                            {
    334                                int var14 = var13.getRenderType();
    335
    336                                if (var14 == 11 || var12 == Block.fenceGate.blockID || var14 == 32)
    337                                {
    338                                    return -3;
    339                                }
    340
    341                                if (var12 == Block.trapdoor.blockID)
    342                                {
    343                                    return -4;
    344                                }
    345
    346                                Material var15 = var13.blockMaterial;
    347
    348                                if (var15 != Material.lava)
    349                                {
    350                                    return 0;
    351                                }
    352
    353                                if (!par0Entity.handleLavaMovement())
    354                                {
    355                                    return -2;
    356                                }
    357                            }
    358                        }
    359                    }
    360                }
    361            }
    362
    363            return var8 ? 2 : 1;
    364        }
    365
    366        /**
    367         * Returns a new PathEntity for a given start and end point
    368         */
    369        private PathEntity createEntityPath(PathPoint par1PathPoint, PathPoint par2PathPoint)
    370        {
    371            int var3 = 1;
    372            PathPoint var4;
    373
    374            for (var4 = par2PathPoint; var4.previous != null; var4 = var4.previous)
    375            {
    376                ++var3;
    377            }
    378
    379            PathPoint[] var5 = new PathPoint[var3];
    380            var4 = par2PathPoint;
    381            --var3;
    382
    383            for (var5[var3] = par2PathPoint; var4.previous != null; var5[var3] = var4)
    384            {
    385                var4 = var4.previous;
    386                --var3;
    387            }
    388
    389            return new PathEntity(var5);
    390        }
    391    }