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