001    package net.minecraft.pathfinding;
002    
003    import net.minecraft.block.Block;
004    import net.minecraft.block.material.Material;
005    import net.minecraft.entity.EntityLiving;
006    import net.minecraft.util.MathHelper;
007    import net.minecraft.util.Vec3;
008    import net.minecraft.world.World;
009    
010    public class PathNavigate
011    {
012        private EntityLiving theEntity;
013        private World worldObj;
014    
015        /** The PathEntity being followed. */
016        private PathEntity currentPath;
017        private float speed;
018    
019        /**
020         * The number of blocks (extra) +/- in each axis that get pulled out as cache for the pathfinder's search space
021         */
022        private float pathSearchRange;
023        private boolean noSunPathfind = false;
024    
025        /** Time, in number of ticks, following the current path */
026        private int totalTicks;
027    
028        /**
029         * The time when the last position check was done (to detect successful movement)
030         */
031        private int ticksAtLastPos;
032    
033        /**
034         * Coordinates of the entity's position last time a check was done (part of monitoring getting 'stuck')
035         */
036        private Vec3 lastPosCheck = Vec3.createVectorHelper(0.0D, 0.0D, 0.0D);
037    
038        /**
039         * Specifically, if a wooden door block is even considered to be passable by the pathfinder
040         */
041        private boolean canPassOpenWoodenDoors = true;
042    
043        /** If door blocks are considered passable even when closed */
044        private boolean canPassClosedWoodenDoors = false;
045    
046        /** If water blocks are avoided (at least by the pathfinder) */
047        private boolean avoidsWater = false;
048    
049        /**
050         * If the entity can swim. Swimming AI enables this and the pathfinder will also cause the entity to swim straight
051         * upwards when underwater
052         */
053        private boolean canSwim = false;
054    
055        public PathNavigate(EntityLiving par1EntityLiving, World par2World, float par3)
056        {
057            this.theEntity = par1EntityLiving;
058            this.worldObj = par2World;
059            this.pathSearchRange = par3;
060        }
061    
062        public void setAvoidsWater(boolean par1)
063        {
064            this.avoidsWater = par1;
065        }
066    
067        public boolean getAvoidsWater()
068        {
069            return this.avoidsWater;
070        }
071    
072        public void setBreakDoors(boolean par1)
073        {
074            this.canPassClosedWoodenDoors = par1;
075        }
076    
077        /**
078         * Sets if the entity can enter open doors
079         */
080        public void setEnterDoors(boolean par1)
081        {
082            this.canPassOpenWoodenDoors = par1;
083        }
084    
085        /**
086         * Returns true if the entity can break doors, false otherwise
087         */
088        public boolean getCanBreakDoors()
089        {
090            return this.canPassClosedWoodenDoors;
091        }
092    
093        /**
094         * Sets if the path should avoid sunlight
095         */
096        public void setAvoidSun(boolean par1)
097        {
098            this.noSunPathfind = par1;
099        }
100    
101        /**
102         * Sets the speed
103         */
104        public void setSpeed(float par1)
105        {
106            this.speed = par1;
107        }
108    
109        /**
110         * Sets if the entity can swim
111         */
112        public void setCanSwim(boolean par1)
113        {
114            this.canSwim = par1;
115        }
116    
117        /**
118         * Returns the path to the given coordinates
119         */
120        public PathEntity getPathToXYZ(double par1, double par3, double par5)
121        {
122            return !this.canNavigate() ? null : this.worldObj.getEntityPathToXYZ(this.theEntity, MathHelper.floor_double(par1), (int)par3, MathHelper.floor_double(par5), this.pathSearchRange, this.canPassOpenWoodenDoors, this.canPassClosedWoodenDoors, this.avoidsWater, this.canSwim);
123        }
124    
125        /**
126         * Try to find and set a path to XYZ. Returns true if successful.
127         */
128        public boolean tryMoveToXYZ(double par1, double par3, double par5, float par7)
129        {
130            PathEntity var8 = this.getPathToXYZ((double)MathHelper.floor_double(par1), (double)((int)par3), (double)MathHelper.floor_double(par5));
131            return this.setPath(var8, par7);
132        }
133    
134        /**
135         * Returns the path to the given EntityLiving
136         */
137        public PathEntity getPathToEntityLiving(EntityLiving par1EntityLiving)
138        {
139            return !this.canNavigate() ? null : this.worldObj.getPathEntityToEntity(this.theEntity, par1EntityLiving, this.pathSearchRange, this.canPassOpenWoodenDoors, this.canPassClosedWoodenDoors, this.avoidsWater, this.canSwim);
140        }
141    
142        /**
143         * Try to find and set a path to EntityLiving. Returns true if successful.
144         */
145        public boolean tryMoveToEntityLiving(EntityLiving par1EntityLiving, float par2)
146        {
147            PathEntity var3 = this.getPathToEntityLiving(par1EntityLiving);
148            return var3 != null ? this.setPath(var3, par2) : false;
149        }
150    
151        /**
152         * sets the active path data if path is 100% unique compared to old path, checks to adjust path for sun avoiding
153         * ents and stores end coords
154         */
155        public boolean setPath(PathEntity par1PathEntity, float par2)
156        {
157            if (par1PathEntity == null)
158            {
159                this.currentPath = null;
160                return false;
161            }
162            else
163            {
164                if (!par1PathEntity.isSamePath(this.currentPath))
165                {
166                    this.currentPath = par1PathEntity;
167                }
168    
169                if (this.noSunPathfind)
170                {
171                    this.removeSunnyPath();
172                }
173    
174                if (this.currentPath.getCurrentPathLength() == 0)
175                {
176                    return false;
177                }
178                else
179                {
180                    this.speed = par2;
181                    Vec3 var3 = this.getEntityPosition();
182                    this.ticksAtLastPos = this.totalTicks;
183                    this.lastPosCheck.xCoord = var3.xCoord;
184                    this.lastPosCheck.yCoord = var3.yCoord;
185                    this.lastPosCheck.zCoord = var3.zCoord;
186                    return true;
187                }
188            }
189        }
190    
191        /**
192         * gets the actively used PathEntity
193         */
194        public PathEntity getPath()
195        {
196            return this.currentPath;
197        }
198    
199        public void onUpdateNavigation()
200        {
201            ++this.totalTicks;
202    
203            if (!this.noPath())
204            {
205                if (this.canNavigate())
206                {
207                    this.pathFollow();
208                }
209    
210                if (!this.noPath())
211                {
212                    Vec3 var1 = this.currentPath.getPosition(this.theEntity);
213    
214                    if (var1 != null)
215                    {
216                        this.theEntity.getMoveHelper().setMoveTo(var1.xCoord, var1.yCoord, var1.zCoord, this.speed);
217                    }
218                }
219            }
220        }
221    
222        private void pathFollow()
223        {
224            Vec3 var1 = this.getEntityPosition();
225            int var2 = this.currentPath.getCurrentPathLength();
226    
227            for (int var3 = this.currentPath.getCurrentPathIndex(); var3 < this.currentPath.getCurrentPathLength(); ++var3)
228            {
229                if (this.currentPath.getPathPointFromIndex(var3).yCoord != (int)var1.yCoord)
230                {
231                    var2 = var3;
232                    break;
233                }
234            }
235    
236            float var8 = this.theEntity.width * this.theEntity.width;
237            int var4;
238    
239            for (var4 = this.currentPath.getCurrentPathIndex(); var4 < var2; ++var4)
240            {
241                if (var1.squareDistanceTo(this.currentPath.getVectorFromIndex(this.theEntity, var4)) < (double)var8)
242                {
243                    this.currentPath.setCurrentPathIndex(var4 + 1);
244                }
245            }
246    
247            var4 = MathHelper.ceiling_float_int(this.theEntity.width);
248            int var5 = (int)this.theEntity.height + 1;
249            int var6 = var4;
250    
251            for (int var7 = var2 - 1; var7 >= this.currentPath.getCurrentPathIndex(); --var7)
252            {
253                if (this.isDirectPathBetweenPoints(var1, this.currentPath.getVectorFromIndex(this.theEntity, var7), var4, var5, var6))
254                {
255                    this.currentPath.setCurrentPathIndex(var7);
256                    break;
257                }
258            }
259    
260            if (this.totalTicks - this.ticksAtLastPos > 100)
261            {
262                if (var1.squareDistanceTo(this.lastPosCheck) < 2.25D)
263                {
264                    this.clearPathEntity();
265                }
266    
267                this.ticksAtLastPos = this.totalTicks;
268                this.lastPosCheck.xCoord = var1.xCoord;
269                this.lastPosCheck.yCoord = var1.yCoord;
270                this.lastPosCheck.zCoord = var1.zCoord;
271            }
272        }
273    
274        /**
275         * If null path or reached the end
276         */
277        public boolean noPath()
278        {
279            return this.currentPath == null || this.currentPath.isFinished();
280        }
281    
282        /**
283         * sets active PathEntity to null
284         */
285        public void clearPathEntity()
286        {
287            this.currentPath = null;
288        }
289    
290        private Vec3 getEntityPosition()
291        {
292            return this.worldObj.getWorldVec3Pool().getVecFromPool(this.theEntity.posX, (double)this.getPathableYPos(), this.theEntity.posZ);
293        }
294    
295        /**
296         * Gets the safe pathing Y position for the entity depending on if it can path swim or not
297         */
298        private int getPathableYPos()
299        {
300            if (this.theEntity.isInWater() && this.canSwim)
301            {
302                int var1 = (int)this.theEntity.boundingBox.minY;
303                int var2 = this.worldObj.getBlockId(MathHelper.floor_double(this.theEntity.posX), var1, MathHelper.floor_double(this.theEntity.posZ));
304                int var3 = 0;
305    
306                do
307                {
308                    if (var2 != Block.waterMoving.blockID && var2 != Block.waterStill.blockID)
309                    {
310                        return var1;
311                    }
312    
313                    ++var1;
314                    var2 = this.worldObj.getBlockId(MathHelper.floor_double(this.theEntity.posX), var1, MathHelper.floor_double(this.theEntity.posZ));
315                    ++var3;
316                }
317                while (var3 <= 16);
318    
319                return (int)this.theEntity.boundingBox.minY;
320            }
321            else
322            {
323                return (int)(this.theEntity.boundingBox.minY + 0.5D);
324            }
325        }
326    
327        /**
328         * If on ground or swimming and can swim
329         */
330        private boolean canNavigate()
331        {
332            return this.theEntity.onGround || this.canSwim && this.isInFluid();
333        }
334    
335        /**
336         * Returns true if the entity is in water or lava, false otherwise
337         */
338        private boolean isInFluid()
339        {
340            return this.theEntity.isInWater() || this.theEntity.handleLavaMovement();
341        }
342    
343        /**
344         * Trims path data from the end to the first sun covered block
345         */
346        private void removeSunnyPath()
347        {
348            if (!this.worldObj.canBlockSeeTheSky(MathHelper.floor_double(this.theEntity.posX), (int)(this.theEntity.boundingBox.minY + 0.5D), MathHelper.floor_double(this.theEntity.posZ)))
349            {
350                for (int var1 = 0; var1 < this.currentPath.getCurrentPathLength(); ++var1)
351                {
352                    PathPoint var2 = this.currentPath.getPathPointFromIndex(var1);
353    
354                    if (this.worldObj.canBlockSeeTheSky(var2.xCoord, var2.yCoord, var2.zCoord))
355                    {
356                        this.currentPath.setCurrentPathLength(var1 - 1);
357                        return;
358                    }
359                }
360            }
361        }
362    
363        /**
364         * Returns true when an entity of specified size could safely walk in a straight line between the two points. Args:
365         * pos1, pos2, entityXSize, entityYSize, entityZSize
366         */
367        private boolean isDirectPathBetweenPoints(Vec3 par1Vec3, Vec3 par2Vec3, int par3, int par4, int par5)
368        {
369            int var6 = MathHelper.floor_double(par1Vec3.xCoord);
370            int var7 = MathHelper.floor_double(par1Vec3.zCoord);
371            double var8 = par2Vec3.xCoord - par1Vec3.xCoord;
372            double var10 = par2Vec3.zCoord - par1Vec3.zCoord;
373            double var12 = var8 * var8 + var10 * var10;
374    
375            if (var12 < 1.0E-8D)
376            {
377                return false;
378            }
379            else
380            {
381                double var14 = 1.0D / Math.sqrt(var12);
382                var8 *= var14;
383                var10 *= var14;
384                par3 += 2;
385                par5 += 2;
386    
387                if (!this.isSafeToStandAt(var6, (int)par1Vec3.yCoord, var7, par3, par4, par5, par1Vec3, var8, var10))
388                {
389                    return false;
390                }
391                else
392                {
393                    par3 -= 2;
394                    par5 -= 2;
395                    double var16 = 1.0D / Math.abs(var8);
396                    double var18 = 1.0D / Math.abs(var10);
397                    double var20 = (double)(var6 * 1) - par1Vec3.xCoord;
398                    double var22 = (double)(var7 * 1) - par1Vec3.zCoord;
399    
400                    if (var8 >= 0.0D)
401                    {
402                        ++var20;
403                    }
404    
405                    if (var10 >= 0.0D)
406                    {
407                        ++var22;
408                    }
409    
410                    var20 /= var8;
411                    var22 /= var10;
412                    int var24 = var8 < 0.0D ? -1 : 1;
413                    int var25 = var10 < 0.0D ? -1 : 1;
414                    int var26 = MathHelper.floor_double(par2Vec3.xCoord);
415                    int var27 = MathHelper.floor_double(par2Vec3.zCoord);
416                    int var28 = var26 - var6;
417                    int var29 = var27 - var7;
418    
419                    do
420                    {
421                        if (var28 * var24 <= 0 && var29 * var25 <= 0)
422                        {
423                            return true;
424                        }
425    
426                        if (var20 < var22)
427                        {
428                            var20 += var16;
429                            var6 += var24;
430                            var28 = var26 - var6;
431                        }
432                        else
433                        {
434                            var22 += var18;
435                            var7 += var25;
436                            var29 = var27 - var7;
437                        }
438                    }
439                    while (this.isSafeToStandAt(var6, (int)par1Vec3.yCoord, var7, par3, par4, par5, par1Vec3, var8, var10));
440    
441                    return false;
442                }
443            }
444        }
445    
446        /**
447         * Returns true when an entity could stand at a position, including solid blocks under the entire entity. Args:
448         * xOffset, yOffset, zOffset, entityXSize, entityYSize, entityZSize, originPosition, vecX, vecZ
449         */
450        private boolean isSafeToStandAt(int par1, int par2, int par3, int par4, int par5, int par6, Vec3 par7Vec3, double par8, double par10)
451        {
452            int var12 = par1 - par4 / 2;
453            int var13 = par3 - par6 / 2;
454    
455            if (!this.isPositionClear(var12, par2, var13, par4, par5, par6, par7Vec3, par8, par10))
456            {
457                return false;
458            }
459            else
460            {
461                for (int var14 = var12; var14 < var12 + par4; ++var14)
462                {
463                    for (int var15 = var13; var15 < var13 + par6; ++var15)
464                    {
465                        double var16 = (double)var14 + 0.5D - par7Vec3.xCoord;
466                        double var18 = (double)var15 + 0.5D - par7Vec3.zCoord;
467    
468                        if (var16 * par8 + var18 * par10 >= 0.0D)
469                        {
470                            int var20 = this.worldObj.getBlockId(var14, par2 - 1, var15);
471    
472                            if (var20 <= 0)
473                            {
474                                return false;
475                            }
476    
477                            Material var21 = Block.blocksList[var20].blockMaterial;
478    
479                            if (var21 == Material.water && !this.theEntity.isInWater())
480                            {
481                                return false;
482                            }
483    
484                            if (var21 == Material.lava)
485                            {
486                                return false;
487                            }
488                        }
489                    }
490                }
491    
492                return true;
493            }
494        }
495    
496        /**
497         * Returns true if an entity does not collide with any solid blocks at the position. Args: xOffset, yOffset,
498         * zOffset, entityXSize, entityYSize, entityZSize, originPosition, vecX, vecZ
499         */
500        private boolean isPositionClear(int par1, int par2, int par3, int par4, int par5, int par6, Vec3 par7Vec3, double par8, double par10)
501        {
502            for (int var12 = par1; var12 < par1 + par4; ++var12)
503            {
504                for (int var13 = par2; var13 < par2 + par5; ++var13)
505                {
506                    for (int var14 = par3; var14 < par3 + par6; ++var14)
507                    {
508                        double var15 = (double)var12 + 0.5D - par7Vec3.xCoord;
509                        double var17 = (double)var14 + 0.5D - par7Vec3.zCoord;
510    
511                        if (var15 * par8 + var17 * par10 >= 0.0D)
512                        {
513                            int var19 = this.worldObj.getBlockId(var12, var13, var14);
514    
515                            if (var19 > 0 && !Block.blocksList[var19].getBlocksMovement(this.worldObj, var12, var13, var14))
516                            {
517                                return false;
518                            }
519                        }
520                    }
521                }
522            }
523    
524            return true;
525        }
526    }