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 }