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