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 }