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