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 }