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    }