001package net.minecraft.pathfinding; 002 003import net.minecraft.block.Block; 004import net.minecraft.block.material.Material; 005import net.minecraft.entity.Entity; 006import net.minecraft.util.IntHashMap; 007import net.minecraft.util.MathHelper; 008import net.minecraft.world.IBlockAccess; 009 010public class PathFinder 011{ 012 /** Used to find obstacles */ 013 private IBlockAccess worldMap; 014 015 /** The path being generated */ 016 private Path path = new Path(); 017 018 /** The points in the path */ 019 private IntHashMap pointMap = new IntHashMap(); 020 021 /** Selection of path points to add to the path */ 022 private PathPoint[] pathOptions = new PathPoint[32]; 023 024 /** should the PathFinder go through wodden door blocks */ 025 private boolean isWoddenDoorAllowed; 026 027 /** 028 * should the PathFinder disregard BlockMovement type materials in its path 029 */ 030 private boolean isMovementBlockAllowed; 031 private boolean isPathingInWater; 032 033 /** tells the FathFinder to not stop pathing underwater */ 034 private boolean canEntityDrown; 035 036 public PathFinder(IBlockAccess par1IBlockAccess, boolean par2, boolean par3, boolean par4, boolean par5) 037 { 038 this.worldMap = par1IBlockAccess; 039 this.isWoddenDoorAllowed = par2; 040 this.isMovementBlockAllowed = par3; 041 this.isPathingInWater = par4; 042 this.canEntityDrown = par5; 043 } 044 045 /** 046 * Creates a path from one entity to another within a minimum distance 047 */ 048 public PathEntity createEntityPathTo(Entity par1Entity, Entity par2Entity, float par3) 049 { 050 return this.createEntityPathTo(par1Entity, par2Entity.posX, par2Entity.boundingBox.minY, par2Entity.posZ, par3); 051 } 052 053 /** 054 * Creates a path from an entity to a specified location within a minimum distance 055 */ 056 public PathEntity createEntityPathTo(Entity par1Entity, int par2, int par3, int par4, float par5) 057 { 058 return this.createEntityPathTo(par1Entity, (double)((float)par2 + 0.5F), (double)((float)par3 + 0.5F), (double)((float)par4 + 0.5F), par5); 059 } 060 061 /** 062 * Internal implementation of creating a path from an entity to a point 063 */ 064 private PathEntity createEntityPathTo(Entity par1Entity, double par2, double par4, double par6, float par8) 065 { 066 this.path.clearPath(); 067 this.pointMap.clearMap(); 068 boolean flag = this.isPathingInWater; 069 int i = MathHelper.floor_double(par1Entity.boundingBox.minY + 0.5D); 070 071 if (this.canEntityDrown && par1Entity.isInWater()) 072 { 073 i = (int)par1Entity.boundingBox.minY; 074 075 for (int j = this.worldMap.getBlockId(MathHelper.floor_double(par1Entity.posX), i, MathHelper.floor_double(par1Entity.posZ)); j == Block.waterMoving.blockID || j == Block.waterStill.blockID; j = this.worldMap.getBlockId(MathHelper.floor_double(par1Entity.posX), i, MathHelper.floor_double(par1Entity.posZ))) 076 { 077 ++i; 078 } 079 080 flag = this.isPathingInWater; 081 this.isPathingInWater = false; 082 } 083 else 084 { 085 i = MathHelper.floor_double(par1Entity.boundingBox.minY + 0.5D); 086 } 087 088 PathPoint pathpoint = this.openPoint(MathHelper.floor_double(par1Entity.boundingBox.minX), i, MathHelper.floor_double(par1Entity.boundingBox.minZ)); 089 PathPoint pathpoint1 = this.openPoint(MathHelper.floor_double(par2 - (double)(par1Entity.width / 2.0F)), MathHelper.floor_double(par4), MathHelper.floor_double(par6 - (double)(par1Entity.width / 2.0F))); 090 PathPoint pathpoint2 = new PathPoint(MathHelper.floor_float(par1Entity.width + 1.0F), MathHelper.floor_float(par1Entity.height + 1.0F), MathHelper.floor_float(par1Entity.width + 1.0F)); 091 PathEntity pathentity = this.addToPath(par1Entity, pathpoint, pathpoint1, pathpoint2, par8); 092 this.isPathingInWater = flag; 093 return pathentity; 094 } 095 096 /** 097 * Adds a path from start to end and returns the whole path (args: unused, start, end, unused, maxDistance) 098 */ 099 private PathEntity addToPath(Entity par1Entity, PathPoint par2PathPoint, PathPoint par3PathPoint, PathPoint par4PathPoint, float par5) 100 { 101 par2PathPoint.totalPathDistance = 0.0F; 102 par2PathPoint.distanceToNext = par2PathPoint.func_75832_b(par3PathPoint); 103 par2PathPoint.distanceToTarget = par2PathPoint.distanceToNext; 104 this.path.clearPath(); 105 this.path.addPoint(par2PathPoint); 106 PathPoint pathpoint3 = par2PathPoint; 107 108 while (!this.path.isPathEmpty()) 109 { 110 PathPoint pathpoint4 = this.path.dequeue(); 111 112 if (pathpoint4.equals(par3PathPoint)) 113 { 114 return this.createEntityPath(par2PathPoint, par3PathPoint); 115 } 116 117 if (pathpoint4.func_75832_b(par3PathPoint) < pathpoint3.func_75832_b(par3PathPoint)) 118 { 119 pathpoint3 = pathpoint4; 120 } 121 122 pathpoint4.isFirst = true; 123 int i = this.findPathOptions(par1Entity, pathpoint4, par4PathPoint, par3PathPoint, par5); 124 125 for (int j = 0; j < i; ++j) 126 { 127 PathPoint pathpoint5 = this.pathOptions[j]; 128 float f1 = pathpoint4.totalPathDistance + pathpoint4.func_75832_b(pathpoint5); 129 130 if (!pathpoint5.isAssigned() || f1 < pathpoint5.totalPathDistance) 131 { 132 pathpoint5.previous = pathpoint4; 133 pathpoint5.totalPathDistance = f1; 134 pathpoint5.distanceToNext = pathpoint5.func_75832_b(par3PathPoint); 135 136 if (pathpoint5.isAssigned()) 137 { 138 this.path.changeDistance(pathpoint5, pathpoint5.totalPathDistance + pathpoint5.distanceToNext); 139 } 140 else 141 { 142 pathpoint5.distanceToTarget = pathpoint5.totalPathDistance + pathpoint5.distanceToNext; 143 this.path.addPoint(pathpoint5); 144 } 145 } 146 } 147 } 148 149 if (pathpoint3 == par2PathPoint) 150 { 151 return null; 152 } 153 else 154 { 155 return this.createEntityPath(par2PathPoint, pathpoint3); 156 } 157 } 158 159 /** 160 * populates pathOptions with available points and returns the number of options found (args: unused1, currentPoint, 161 * unused2, targetPoint, maxDistance) 162 */ 163 private int findPathOptions(Entity par1Entity, PathPoint par2PathPoint, PathPoint par3PathPoint, PathPoint par4PathPoint, float par5) 164 { 165 int i = 0; 166 byte b0 = 0; 167 168 if (this.getVerticalOffset(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord + 1, par2PathPoint.zCoord, par3PathPoint) == 1) 169 { 170 b0 = 1; 171 } 172 173 PathPoint pathpoint3 = this.getSafePoint(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord, par2PathPoint.zCoord + 1, par3PathPoint, b0); 174 PathPoint pathpoint4 = this.getSafePoint(par1Entity, par2PathPoint.xCoord - 1, par2PathPoint.yCoord, par2PathPoint.zCoord, par3PathPoint, b0); 175 PathPoint pathpoint5 = this.getSafePoint(par1Entity, par2PathPoint.xCoord + 1, par2PathPoint.yCoord, par2PathPoint.zCoord, par3PathPoint, b0); 176 PathPoint pathpoint6 = this.getSafePoint(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord, par2PathPoint.zCoord - 1, par3PathPoint, b0); 177 178 if (pathpoint3 != null && !pathpoint3.isFirst && pathpoint3.distanceTo(par4PathPoint) < par5) 179 { 180 this.pathOptions[i++] = pathpoint3; 181 } 182 183 if (pathpoint4 != null && !pathpoint4.isFirst && pathpoint4.distanceTo(par4PathPoint) < par5) 184 { 185 this.pathOptions[i++] = pathpoint4; 186 } 187 188 if (pathpoint5 != null && !pathpoint5.isFirst && pathpoint5.distanceTo(par4PathPoint) < par5) 189 { 190 this.pathOptions[i++] = pathpoint5; 191 } 192 193 if (pathpoint6 != null && !pathpoint6.isFirst && pathpoint6.distanceTo(par4PathPoint) < par5) 194 { 195 this.pathOptions[i++] = pathpoint6; 196 } 197 198 return i; 199 } 200 201 /** 202 * Returns a point that the entity can safely move to 203 */ 204 private PathPoint getSafePoint(Entity par1Entity, int par2, int par3, int par4, PathPoint par5PathPoint, int par6) 205 { 206 PathPoint pathpoint1 = null; 207 int i1 = this.getVerticalOffset(par1Entity, par2, par3, par4, par5PathPoint); 208 209 if (i1 == 2) 210 { 211 return this.openPoint(par2, par3, par4); 212 } 213 else 214 { 215 if (i1 == 1) 216 { 217 pathpoint1 = this.openPoint(par2, par3, par4); 218 } 219 220 if (pathpoint1 == null && par6 > 0 && i1 != -3 && i1 != -4 && this.getVerticalOffset(par1Entity, par2, par3 + par6, par4, par5PathPoint) == 1) 221 { 222 pathpoint1 = this.openPoint(par2, par3 + par6, par4); 223 par3 += par6; 224 } 225 226 if (pathpoint1 != null) 227 { 228 int j1 = 0; 229 int k1 = 0; 230 231 while (par3 > 0) 232 { 233 k1 = this.getVerticalOffset(par1Entity, par2, par3 - 1, par4, par5PathPoint); 234 235 if (this.isPathingInWater && k1 == -1) 236 { 237 return null; 238 } 239 240 if (k1 != 1) 241 { 242 break; 243 } 244 245 if (j1++ >= par1Entity.func_82143_as()) 246 { 247 return null; 248 } 249 250 --par3; 251 252 if (par3 > 0) 253 { 254 pathpoint1 = this.openPoint(par2, par3, par4); 255 } 256 } 257 258 if (k1 == -2) 259 { 260 return null; 261 } 262 } 263 264 return pathpoint1; 265 } 266 } 267 268 /** 269 * Returns a mapped point or creates and adds one 270 */ 271 private final PathPoint openPoint(int par1, int par2, int par3) 272 { 273 int l = PathPoint.makeHash(par1, par2, par3); 274 PathPoint pathpoint = (PathPoint)this.pointMap.lookup(l); 275 276 if (pathpoint == null) 277 { 278 pathpoint = new PathPoint(par1, par2, par3); 279 this.pointMap.addKey(l, pathpoint); 280 } 281 282 return pathpoint; 283 } 284 285 /** 286 * Checks if an entity collides with blocks at a position. Returns 1 if clear, 0 for colliding with any solid block, 287 * -1 for water(if avoiding water) but otherwise clear, -2 for lava, -3 for fence, -4 for closed trapdoor, 2 if 288 * otherwise clear except for open trapdoor or water(if not avoiding) 289 */ 290 public int getVerticalOffset(Entity par1Entity, int par2, int par3, int par4, PathPoint par5PathPoint) 291 { 292 return func_82565_a(par1Entity, par2, par3, par4, par5PathPoint, this.isPathingInWater, this.isMovementBlockAllowed, this.isWoddenDoorAllowed); 293 } 294 295 public static int func_82565_a(Entity par0Entity, int par1, int par2, int par3, PathPoint par4PathPoint, boolean par5, boolean par6, boolean par7) 296 { 297 boolean flag3 = false; 298 299 for (int l = par1; l < par1 + par4PathPoint.xCoord; ++l) 300 { 301 for (int i1 = par2; i1 < par2 + par4PathPoint.yCoord; ++i1) 302 { 303 for (int j1 = par3; j1 < par3 + par4PathPoint.zCoord; ++j1) 304 { 305 int k1 = par0Entity.worldObj.getBlockId(l, i1, j1); 306 307 if (k1 > 0) 308 { 309 if (k1 == Block.trapdoor.blockID) 310 { 311 flag3 = true; 312 } 313 else if (k1 != Block.waterMoving.blockID && k1 != Block.waterStill.blockID) 314 { 315 if (!par7 && k1 == Block.doorWood.blockID) 316 { 317 return 0; 318 } 319 } 320 else 321 { 322 if (par5) 323 { 324 return -1; 325 } 326 327 flag3 = true; 328 } 329 330 Block block = Block.blocksList[k1]; 331 int l1 = block.getRenderType(); 332 333 if (par0Entity.worldObj.blockGetRenderType(l, i1, j1) == 9) 334 { 335 int i2 = MathHelper.floor_double(par0Entity.posX); 336 int j2 = MathHelper.floor_double(par0Entity.posY); 337 int k2 = MathHelper.floor_double(par0Entity.posZ); 338 339 if (par0Entity.worldObj.blockGetRenderType(i2, j2, k2) != 9 && par0Entity.worldObj.blockGetRenderType(i2, j2 - 1, k2) != 9) 340 { 341 return -3; 342 } 343 } 344 else if (!block.getBlocksMovement(par0Entity.worldObj, l, i1, j1) && (!par6 || k1 != Block.doorWood.blockID)) 345 { 346 if (l1 == 11 || k1 == Block.fenceGate.blockID || l1 == 32) 347 { 348 return -3; 349 } 350 351 if (k1 == Block.trapdoor.blockID) 352 { 353 return -4; 354 } 355 356 Material material = block.blockMaterial; 357 358 if (material != Material.lava) 359 { 360 return 0; 361 } 362 363 if (!par0Entity.handleLavaMovement()) 364 { 365 return -2; 366 } 367 } 368 } 369 } 370 } 371 } 372 373 return flag3 ? 2 : 1; 374 } 375 376 /** 377 * Returns a new PathEntity for a given start and end point 378 */ 379 private PathEntity createEntityPath(PathPoint par1PathPoint, PathPoint par2PathPoint) 380 { 381 int i = 1; 382 PathPoint pathpoint2; 383 384 for (pathpoint2 = par2PathPoint; pathpoint2.previous != null; pathpoint2 = pathpoint2.previous) 385 { 386 ++i; 387 } 388 389 PathPoint[] apathpoint = new PathPoint[i]; 390 pathpoint2 = par2PathPoint; 391 --i; 392 393 for (apathpoint[i] = par2PathPoint; pathpoint2.previous != null; apathpoint[i] = pathpoint2) 394 { 395 pathpoint2 = pathpoint2.previous; 396 --i; 397 } 398 399 return new PathEntity(apathpoint); 400 } 401}