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 }