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.getWorldVec3Pool().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.getGameRules().getGameRuleBooleanValue("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.getWorldVec3Pool().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 /** 445 * Returns the amount of damage a mob should deal. 446 */ 447 public int getAttackStrength(Entity par1Entity) 448 { 449 return 7; 450 } 451 452 static 453 { 454 carriableBlocks[Block.grass.blockID] = true; 455 carriableBlocks[Block.dirt.blockID] = true; 456 carriableBlocks[Block.sand.blockID] = true; 457 carriableBlocks[Block.gravel.blockID] = true; 458 carriableBlocks[Block.plantYellow.blockID] = true; 459 carriableBlocks[Block.plantRed.blockID] = true; 460 carriableBlocks[Block.mushroomBrown.blockID] = true; 461 carriableBlocks[Block.mushroomRed.blockID] = true; 462 carriableBlocks[Block.tnt.blockID] = true; 463 carriableBlocks[Block.cactus.blockID] = true; 464 carriableBlocks[Block.blockClay.blockID] = true; 465 carriableBlocks[Block.pumpkin.blockID] = true; 466 carriableBlocks[Block.melon.blockID] = true; 467 carriableBlocks[Block.mycelium.blockID] = true; 468 } 469 }