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    }