001 package net.minecraft.src;
002
003 public class EntityEnderman extends EntityMob
004 {
005 public static boolean[] carriableBlocks = new boolean[256];
006
007 /**
008 * Counter to delay the teleportation of an enderman towards the currently attacked target
009 */
010 private int teleportDelay = 0;
011 private int field_70826_g = 0;
012
013 public EntityEnderman(World par1World)
014 {
015 super(par1World);
016 this.texture = "/mob/enderman.png";
017 this.moveSpeed = 0.2F;
018 this.setSize(0.6F, 2.9F);
019 this.stepHeight = 1.0F;
020 }
021
022 public int getMaxHealth()
023 {
024 return 40;
025 }
026
027 protected void entityInit()
028 {
029 super.entityInit();
030 this.dataWatcher.addObject(16, new Byte((byte)0));
031 this.dataWatcher.addObject(17, new Byte((byte)0));
032 this.dataWatcher.addObject(18, new Byte((byte)0));
033 }
034
035 /**
036 * (abstract) Protected helper method to write subclass entity data to NBT.
037 */
038 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
039 {
040 super.writeEntityToNBT(par1NBTTagCompound);
041 par1NBTTagCompound.setShort("carried", (short)this.getCarried());
042 par1NBTTagCompound.setShort("carriedData", (short)this.getCarryingData());
043 }
044
045 /**
046 * (abstract) Protected helper method to read subclass entity data from NBT.
047 */
048 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
049 {
050 super.readEntityFromNBT(par1NBTTagCompound);
051 this.setCarried(par1NBTTagCompound.getShort("carried"));
052 this.setCarryingData(par1NBTTagCompound.getShort("carriedData"));
053 }
054
055 /**
056 * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking
057 * (Animals, Spiders at day, peaceful PigZombies).
058 */
059 protected Entity findPlayerToAttack()
060 {
061 EntityPlayer var1 = this.worldObj.getClosestVulnerablePlayerToEntity(this, 64.0D);
062
063 if (var1 != null)
064 {
065 if (this.shouldAttackPlayer(var1))
066 {
067 if (this.field_70826_g == 0)
068 {
069 this.worldObj.playSoundAtEntity(var1, "mob.endermen.stare", 1.0F, 1.0F);
070 }
071
072 if (this.field_70826_g++ == 5)
073 {
074 this.field_70826_g = 0;
075 this.func_70819_e(true);
076 return var1;
077 }
078 }
079 else
080 {
081 this.field_70826_g = 0;
082 }
083 }
084
085 return null;
086 }
087
088 /**
089 * Checks to see if this enderman should be attacking this player
090 */
091 private boolean shouldAttackPlayer(EntityPlayer par1EntityPlayer)
092 {
093 ItemStack var2 = par1EntityPlayer.inventory.armorInventory[3];
094
095 if (var2 != null && var2.itemID == Block.pumpkin.blockID)
096 {
097 return false;
098 }
099 else
100 {
101 Vec3 var3 = par1EntityPlayer.getLook(1.0F).normalize();
102 Vec3 var4 = this.worldObj.func_82732_R().getVecFromPool(this.posX - par1EntityPlayer.posX, this.boundingBox.minY + (double)(this.height / 2.0F) - (par1EntityPlayer.posY + (double)par1EntityPlayer.getEyeHeight()), this.posZ - par1EntityPlayer.posZ);
103 double var5 = var4.lengthVector();
104 var4 = var4.normalize();
105 double var7 = var3.dotProduct(var4);
106 return var7 > 1.0D - 0.025D / var5 ? par1EntityPlayer.canEntityBeSeen(this) : false;
107 }
108 }
109
110 /**
111 * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
112 * use this to react to sunlight and start to burn.
113 */
114 public void onLivingUpdate()
115 {
116 if (this.isWet())
117 {
118 this.attackEntityFrom(DamageSource.drown, 1);
119 }
120
121 this.moveSpeed = this.entityToAttack != null ? 6.5F : 0.3F;
122 int var1;
123
124 if (!this.worldObj.isRemote && this.worldObj.func_82736_K().func_82766_b("mobGriefing"))
125 {
126 int var2;
127 int var3;
128 int var4;
129
130 if (this.getCarried() == 0)
131 {
132 if (this.rand.nextInt(20) == 0)
133 {
134 var1 = MathHelper.floor_double(this.posX - 2.0D + this.rand.nextDouble() * 4.0D);
135 var2 = MathHelper.floor_double(this.posY + this.rand.nextDouble() * 3.0D);
136 var3 = MathHelper.floor_double(this.posZ - 2.0D + this.rand.nextDouble() * 4.0D);
137 var4 = this.worldObj.getBlockId(var1, var2, var3);
138
139 if (carriableBlocks[var4])
140 {
141 this.setCarried(this.worldObj.getBlockId(var1, var2, var3));
142 this.setCarryingData(this.worldObj.getBlockMetadata(var1, var2, var3));
143 this.worldObj.setBlockWithNotify(var1, var2, var3, 0);
144 }
145 }
146 }
147 else if (this.rand.nextInt(2000) == 0)
148 {
149 var1 = MathHelper.floor_double(this.posX - 1.0D + this.rand.nextDouble() * 2.0D);
150 var2 = MathHelper.floor_double(this.posY + this.rand.nextDouble() * 2.0D);
151 var3 = MathHelper.floor_double(this.posZ - 1.0D + this.rand.nextDouble() * 2.0D);
152 var4 = this.worldObj.getBlockId(var1, var2, var3);
153 int var5 = this.worldObj.getBlockId(var1, var2 - 1, var3);
154
155 if (var4 == 0 && var5 > 0 && Block.blocksList[var5].renderAsNormalBlock())
156 {
157 this.worldObj.setBlockAndMetadataWithNotify(var1, var2, var3, this.getCarried(), this.getCarryingData());
158 this.setCarried(0);
159 }
160 }
161 }
162
163 for (var1 = 0; var1 < 2; ++var1)
164 {
165 this.worldObj.spawnParticle("portal", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height - 0.25D, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D);
166 }
167
168 if (this.worldObj.isDaytime() && !this.worldObj.isRemote)
169 {
170 float var6 = this.getBrightness(1.0F);
171
172 if (var6 > 0.5F && this.worldObj.canBlockSeeTheSky(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)) && this.rand.nextFloat() * 30.0F < (var6 - 0.4F) * 2.0F)
173 {
174 this.entityToAttack = null;
175 this.func_70819_e(false);
176 this.teleportRandomly();
177 }
178 }
179
180 if (this.isWet())
181 {
182 this.entityToAttack = null;
183 this.func_70819_e(false);
184 this.teleportRandomly();
185 }
186
187 this.isJumping = false;
188
189 if (this.entityToAttack != null)
190 {
191 this.faceEntity(this.entityToAttack, 100.0F, 100.0F);
192 }
193
194 if (!this.worldObj.isRemote && this.isEntityAlive())
195 {
196 if (this.entityToAttack != null)
197 {
198 if (this.entityToAttack instanceof EntityPlayer && this.shouldAttackPlayer((EntityPlayer)this.entityToAttack))
199 {
200 this.moveStrafing = this.moveForward = 0.0F;
201 this.moveSpeed = 0.0F;
202
203 if (this.entityToAttack.getDistanceSqToEntity(this) < 16.0D)
204 {
205 this.teleportRandomly();
206 }
207
208 this.teleportDelay = 0;
209 }
210 else if (this.entityToAttack.getDistanceSqToEntity(this) > 256.0D && this.teleportDelay++ >= 30 && this.teleportToEntity(this.entityToAttack))
211 {
212 this.teleportDelay = 0;
213 }
214 }
215 else
216 {
217 this.func_70819_e(false);
218 this.teleportDelay = 0;
219 }
220 }
221
222 super.onLivingUpdate();
223 }
224
225 /**
226 * Teleport the enderman to a random nearby position
227 */
228 protected boolean teleportRandomly()
229 {
230 double var1 = this.posX + (this.rand.nextDouble() - 0.5D) * 64.0D;
231 double var3 = this.posY + (double)(this.rand.nextInt(64) - 32);
232 double var5 = this.posZ + (this.rand.nextDouble() - 0.5D) * 64.0D;
233 return this.teleportTo(var1, var3, var5);
234 }
235
236 /**
237 * Teleport the enderman to another entity
238 */
239 protected boolean teleportToEntity(Entity par1Entity)
240 {
241 Vec3 var2 = this.worldObj.func_82732_R().getVecFromPool(this.posX - par1Entity.posX, this.boundingBox.minY + (double)(this.height / 2.0F) - par1Entity.posY + (double)par1Entity.getEyeHeight(), this.posZ - par1Entity.posZ);
242 var2 = var2.normalize();
243 double var3 = 16.0D;
244 double var5 = this.posX + (this.rand.nextDouble() - 0.5D) * 8.0D - var2.xCoord * var3;
245 double var7 = this.posY + (double)(this.rand.nextInt(16) - 8) - var2.yCoord * var3;
246 double var9 = this.posZ + (this.rand.nextDouble() - 0.5D) * 8.0D - var2.zCoord * var3;
247 return this.teleportTo(var5, var7, var9);
248 }
249
250 /**
251 * Teleport the enderman
252 */
253 protected boolean teleportTo(double par1, double par3, double par5)
254 {
255 double var7 = this.posX;
256 double var9 = this.posY;
257 double var11 = this.posZ;
258 this.posX = par1;
259 this.posY = par3;
260 this.posZ = par5;
261 boolean var13 = false;
262 int var14 = MathHelper.floor_double(this.posX);
263 int var15 = MathHelper.floor_double(this.posY);
264 int var16 = MathHelper.floor_double(this.posZ);
265 int var18;
266
267 if (this.worldObj.blockExists(var14, var15, var16))
268 {
269 boolean var17 = false;
270
271 while (!var17 && var15 > 0)
272 {
273 var18 = this.worldObj.getBlockId(var14, var15 - 1, var16);
274
275 if (var18 != 0 && Block.blocksList[var18].blockMaterial.blocksMovement())
276 {
277 var17 = true;
278 }
279 else
280 {
281 --this.posY;
282 --var15;
283 }
284 }
285
286 if (var17)
287 {
288 this.setPosition(this.posX, this.posY, this.posZ);
289
290 if (this.worldObj.getCollidingBoundingBoxes(this, this.boundingBox).isEmpty() && !this.worldObj.isAnyLiquid(this.boundingBox))
291 {
292 var13 = true;
293 }
294 }
295 }
296
297 if (!var13)
298 {
299 this.setPosition(var7, var9, var11);
300 return false;
301 }
302 else
303 {
304 short var30 = 128;
305
306 for (var18 = 0; var18 < var30; ++var18)
307 {
308 double var19 = (double)var18 / ((double)var30 - 1.0D);
309 float var21 = (this.rand.nextFloat() - 0.5F) * 0.2F;
310 float var22 = (this.rand.nextFloat() - 0.5F) * 0.2F;
311 float var23 = (this.rand.nextFloat() - 0.5F) * 0.2F;
312 double var24 = var7 + (this.posX - var7) * var19 + (this.rand.nextDouble() - 0.5D) * (double)this.width * 2.0D;
313 double var26 = var9 + (this.posY - var9) * var19 + this.rand.nextDouble() * (double)this.height;
314 double var28 = var11 + (this.posZ - var11) * var19 + (this.rand.nextDouble() - 0.5D) * (double)this.width * 2.0D;
315 this.worldObj.spawnParticle("portal", var24, var26, var28, (double)var21, (double)var22, (double)var23);
316 }
317
318 this.worldObj.playSoundEffect(var7, var9, var11, "mob.endermen.portal", 1.0F, 1.0F);
319 this.worldObj.playSoundAtEntity(this, "mob.endermen.portal", 1.0F, 1.0F);
320 return true;
321 }
322 }
323
324 /**
325 * Returns the sound this mob makes while it's alive.
326 */
327 protected String getLivingSound()
328 {
329 return this.func_70823_r() ? "mob.endermen.scream" : "mob.endermen.idle";
330 }
331
332 /**
333 * Returns the sound this mob makes when it is hurt.
334 */
335 protected String getHurtSound()
336 {
337 return "mob.endermen.hit";
338 }
339
340 /**
341 * Returns the sound this mob makes on death.
342 */
343 protected String getDeathSound()
344 {
345 return "mob.endermen.death";
346 }
347
348 /**
349 * Returns the item ID for the item the mob drops on death.
350 */
351 protected int getDropItemId()
352 {
353 return Item.enderPearl.shiftedIndex;
354 }
355
356 /**
357 * Drop 0-2 items of this living's type
358 */
359 protected void dropFewItems(boolean par1, int par2)
360 {
361 int var3 = this.getDropItemId();
362
363 if (var3 > 0)
364 {
365 int var4 = this.rand.nextInt(2 + par2);
366
367 for (int var5 = 0; var5 < var4; ++var5)
368 {
369 this.dropItem(var3, 1);
370 }
371 }
372 }
373
374 /**
375 * Set the id of the block an enderman carries
376 */
377 public void setCarried(int par1)
378 {
379 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(par1 & 255)));
380 }
381
382 /**
383 * Get the id of the block an enderman carries
384 */
385 public int getCarried()
386 {
387 return this.dataWatcher.getWatchableObjectByte(16);
388 }
389
390 /**
391 * Set the metadata of the block an enderman carries
392 */
393 public void setCarryingData(int par1)
394 {
395 this.dataWatcher.updateObject(17, Byte.valueOf((byte)(par1 & 255)));
396 }
397
398 /**
399 * Get the metadata of the block an enderman carries
400 */
401 public int getCarryingData()
402 {
403 return this.dataWatcher.getWatchableObjectByte(17);
404 }
405
406 /**
407 * Called when the entity is attacked.
408 */
409 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
410 {
411 if (par1DamageSource instanceof EntityDamageSourceIndirect)
412 {
413 for (int var3 = 0; var3 < 64; ++var3)
414 {
415 if (this.teleportRandomly())
416 {
417 return true;
418 }
419 }
420
421 return false;
422 }
423 else
424 {
425 if (par1DamageSource.getEntity() instanceof EntityPlayer)
426 {
427 this.func_70819_e(true);
428 }
429
430 return super.attackEntityFrom(par1DamageSource, par2);
431 }
432 }
433
434 public boolean func_70823_r()
435 {
436 return this.dataWatcher.getWatchableObjectByte(18) > 0;
437 }
438
439 public void func_70819_e(boolean par1)
440 {
441 this.dataWatcher.updateObject(18, Byte.valueOf((byte)(par1 ? 1 : 0)));
442 }
443
444 public int func_82193_c(Entity par1Entity)
445 {
446 return 7;
447 }
448
449 static
450 {
451 carriableBlocks[Block.grass.blockID] = true;
452 carriableBlocks[Block.dirt.blockID] = true;
453 carriableBlocks[Block.sand.blockID] = true;
454 carriableBlocks[Block.gravel.blockID] = true;
455 carriableBlocks[Block.plantYellow.blockID] = true;
456 carriableBlocks[Block.plantRed.blockID] = true;
457 carriableBlocks[Block.mushroomBrown.blockID] = true;
458 carriableBlocks[Block.mushroomRed.blockID] = true;
459 carriableBlocks[Block.tnt.blockID] = true;
460 carriableBlocks[Block.cactus.blockID] = true;
461 carriableBlocks[Block.blockClay.blockID] = true;
462 carriableBlocks[Block.pumpkin.blockID] = true;
463 carriableBlocks[Block.melon.blockID] = true;
464 carriableBlocks[Block.mycelium.blockID] = true;
465 }
466 }