001package net.minecraft.pathfinding;
002
003import net.minecraft.block.Block;
004import net.minecraft.block.material.Material;
005import net.minecraft.entity.EntityLiving;
006import net.minecraft.util.MathHelper;
007import net.minecraft.util.Vec3;
008import net.minecraft.world.World;
009
010public 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}