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