001 package net.minecraft.pathfinding;
002
003 import net.minecraft.block.Block;
004 import net.minecraft.block.material.Material;
005 import net.minecraft.entity.Entity;
006 import net.minecraft.util.IntHashMap;
007 import net.minecraft.util.MathHelper;
008 import net.minecraft.world.IBlockAccess;
009
010 public 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 var9 = this.isPathingInWater;
069 int var10 = MathHelper.floor_double(par1Entity.boundingBox.minY + 0.5D);
070
071 if (this.canEntityDrown && par1Entity.isInWater())
072 {
073 var10 = (int)par1Entity.boundingBox.minY;
074
075 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)))
076 {
077 ++var10;
078 }
079
080 var9 = this.isPathingInWater;
081 this.isPathingInWater = false;
082 }
083 else
084 {
085 var10 = MathHelper.floor_double(par1Entity.boundingBox.minY + 0.5D);
086 }
087
088 PathPoint var15 = this.openPoint(MathHelper.floor_double(par1Entity.boundingBox.minX), var10, MathHelper.floor_double(par1Entity.boundingBox.minZ));
089 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)));
090 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));
091 PathEntity var14 = this.addToPath(par1Entity, var15, var12, var13, par8);
092 this.isPathingInWater = var9;
093 return var14;
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 var6 = par2PathPoint;
107
108 while (!this.path.isPathEmpty())
109 {
110 PathPoint var7 = this.path.dequeue();
111
112 if (var7.equals(par3PathPoint))
113 {
114 return this.createEntityPath(par2PathPoint, par3PathPoint);
115 }
116
117 if (var7.func_75832_b(par3PathPoint) < var6.func_75832_b(par3PathPoint))
118 {
119 var6 = var7;
120 }
121
122 var7.isFirst = true;
123 int var8 = this.findPathOptions(par1Entity, var7, par4PathPoint, par3PathPoint, par5);
124
125 for (int var9 = 0; var9 < var8; ++var9)
126 {
127 PathPoint var10 = this.pathOptions[var9];
128 float var11 = var7.totalPathDistance + var7.func_75832_b(var10);
129
130 if (!var10.isAssigned() || var11 < var10.totalPathDistance)
131 {
132 var10.previous = var7;
133 var10.totalPathDistance = var11;
134 var10.distanceToNext = var10.func_75832_b(par3PathPoint);
135
136 if (var10.isAssigned())
137 {
138 this.path.changeDistance(var10, var10.totalPathDistance + var10.distanceToNext);
139 }
140 else
141 {
142 var10.distanceToTarget = var10.totalPathDistance + var10.distanceToNext;
143 this.path.addPoint(var10);
144 }
145 }
146 }
147 }
148
149 if (var6 == par2PathPoint)
150 {
151 return null;
152 }
153 else
154 {
155 return this.createEntityPath(par2PathPoint, var6);
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 var6 = 0;
166 byte var7 = 0;
167
168 if (this.getVerticalOffset(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord + 1, par2PathPoint.zCoord, par3PathPoint) == 1)
169 {
170 var7 = 1;
171 }
172
173 PathPoint var8 = this.getSafePoint(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord, par2PathPoint.zCoord + 1, par3PathPoint, var7);
174 PathPoint var9 = this.getSafePoint(par1Entity, par2PathPoint.xCoord - 1, par2PathPoint.yCoord, par2PathPoint.zCoord, par3PathPoint, var7);
175 PathPoint var10 = this.getSafePoint(par1Entity, par2PathPoint.xCoord + 1, par2PathPoint.yCoord, par2PathPoint.zCoord, par3PathPoint, var7);
176 PathPoint var11 = this.getSafePoint(par1Entity, par2PathPoint.xCoord, par2PathPoint.yCoord, par2PathPoint.zCoord - 1, par3PathPoint, var7);
177
178 if (var8 != null && !var8.isFirst && var8.distanceTo(par4PathPoint) < par5)
179 {
180 this.pathOptions[var6++] = var8;
181 }
182
183 if (var9 != null && !var9.isFirst && var9.distanceTo(par4PathPoint) < par5)
184 {
185 this.pathOptions[var6++] = var9;
186 }
187
188 if (var10 != null && !var10.isFirst && var10.distanceTo(par4PathPoint) < par5)
189 {
190 this.pathOptions[var6++] = var10;
191 }
192
193 if (var11 != null && !var11.isFirst && var11.distanceTo(par4PathPoint) < par5)
194 {
195 this.pathOptions[var6++] = var11;
196 }
197
198 return var6;
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 var7 = null;
207 int var8 = this.getVerticalOffset(par1Entity, par2, par3, par4, par5PathPoint);
208
209 if (var8 == 2)
210 {
211 return this.openPoint(par2, par3, par4);
212 }
213 else
214 {
215 if (var8 == 1)
216 {
217 var7 = this.openPoint(par2, par3, par4);
218 }
219
220 if (var7 == null && par6 > 0 && var8 != -3 && var8 != -4 && this.getVerticalOffset(par1Entity, par2, par3 + par6, par4, par5PathPoint) == 1)
221 {
222 var7 = this.openPoint(par2, par3 + par6, par4);
223 par3 += par6;
224 }
225
226 if (var7 != null)
227 {
228 int var9 = 0;
229 int var10 = 0;
230
231 while (par3 > 0)
232 {
233 var10 = this.getVerticalOffset(par1Entity, par2, par3 - 1, par4, par5PathPoint);
234
235 if (this.isPathingInWater && var10 == -1)
236 {
237 return null;
238 }
239
240 if (var10 != 1)
241 {
242 break;
243 }
244
245 if (var9++ >= par1Entity.func_82143_as())
246 {
247 return null;
248 }
249
250 --par3;
251
252 if (par3 > 0)
253 {
254 var7 = this.openPoint(par2, par3, par4);
255 }
256 }
257
258 if (var10 == -2)
259 {
260 return null;
261 }
262 }
263
264 return var7;
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 var4 = PathPoint.makeHash(par1, par2, par3);
274 PathPoint var5 = (PathPoint)this.pointMap.lookup(var4);
275
276 if (var5 == null)
277 {
278 var5 = new PathPoint(par1, par2, par3);
279 this.pointMap.addKey(var4, var5);
280 }
281
282 return var5;
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 var8 = false;
298
299 for (int var9 = par1; var9 < par1 + par4PathPoint.xCoord; ++var9)
300 {
301 for (int var10 = par2; var10 < par2 + par4PathPoint.yCoord; ++var10)
302 {
303 for (int var11 = par3; var11 < par3 + par4PathPoint.zCoord; ++var11)
304 {
305 int var12 = par0Entity.worldObj.getBlockId(var9, var10, var11);
306
307 if (var12 > 0)
308 {
309 if (var12 == Block.trapdoor.blockID)
310 {
311 var8 = true;
312 }
313 else if (var12 != Block.waterMoving.blockID && var12 != Block.waterStill.blockID)
314 {
315 if (!par7 && var12 == Block.doorWood.blockID)
316 {
317 return 0;
318 }
319 }
320 else
321 {
322 if (par5)
323 {
324 return -1;
325 }
326
327 var8 = true;
328 }
329
330 Block var13 = Block.blocksList[var12];
331
332 if (!var13.getBlocksMovement(par0Entity.worldObj, var9, var10, var11) && (!par6 || var12 != Block.doorWood.blockID))
333 {
334 int var14 = var13.getRenderType();
335
336 if (var14 == 11 || var12 == Block.fenceGate.blockID || var14 == 32)
337 {
338 return -3;
339 }
340
341 if (var12 == Block.trapdoor.blockID)
342 {
343 return -4;
344 }
345
346 Material var15 = var13.blockMaterial;
347
348 if (var15 != Material.lava)
349 {
350 return 0;
351 }
352
353 if (!par0Entity.handleLavaMovement())
354 {
355 return -2;
356 }
357 }
358 }
359 }
360 }
361 }
362
363 return var8 ? 2 : 1;
364 }
365
366 /**
367 * Returns a new PathEntity for a given start and end point
368 */
369 private PathEntity createEntityPath(PathPoint par1PathPoint, PathPoint par2PathPoint)
370 {
371 int var3 = 1;
372 PathPoint var4;
373
374 for (var4 = par2PathPoint; var4.previous != null; var4 = var4.previous)
375 {
376 ++var3;
377 }
378
379 PathPoint[] var5 = new PathPoint[var3];
380 var4 = par2PathPoint;
381 --var3;
382
383 for (var5[var3] = par2PathPoint; var4.previous != null; var5[var3] = var4)
384 {
385 var4 = var4.previous;
386 --var3;
387 }
388
389 return new PathEntity(var5);
390 }
391 }