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    }