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 }