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 }