package net.minecraft.entity.monster; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.DamageSource; import net.minecraft.util.EntityDamageSourceIndirect; import net.minecraft.util.MathHelper; import net.minecraft.util.Vec3; import net.minecraft.world.World; public class EntityEnderman extends EntityMob { public static boolean[] carriableBlocks = new boolean[256]; /** * Counter to delay the teleportation of an enderman towards the currently attacked target */ private int teleportDelay = 0; private int field_70826_g = 0; public EntityEnderman(World par1World) { super(par1World); this.texture = "/mob/enderman.png"; this.moveSpeed = 0.2F; this.setSize(0.6F, 2.9F); this.stepHeight = 1.0F; } public int getMaxHealth() { return 40; } protected void entityInit() { super.entityInit(); this.dataWatcher.addObject(16, new Byte((byte)0)); this.dataWatcher.addObject(17, new Byte((byte)0)); this.dataWatcher.addObject(18, new Byte((byte)0)); } /** * (abstract) Protected helper method to write subclass entity data to NBT. */ public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) { super.writeEntityToNBT(par1NBTTagCompound); par1NBTTagCompound.setShort("carried", (short)this.getCarried()); par1NBTTagCompound.setShort("carriedData", (short)this.getCarryingData()); } /** * (abstract) Protected helper method to read subclass entity data from NBT. */ public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) { super.readEntityFromNBT(par1NBTTagCompound); this.setCarried(par1NBTTagCompound.getShort("carried")); this.setCarryingData(par1NBTTagCompound.getShort("carriedData")); } /** * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking * (Animals, Spiders at day, peaceful PigZombies). */ protected Entity findPlayerToAttack() { EntityPlayer var1 = this.worldObj.getClosestVulnerablePlayerToEntity(this, 64.0D); if (var1 != null) { if (this.shouldAttackPlayer(var1)) { if (this.field_70826_g == 0) { this.worldObj.playSoundAtEntity(var1, "mob.endermen.stare", 1.0F, 1.0F); } if (this.field_70826_g++ == 5) { this.field_70826_g = 0; this.func_70819_e(true); return var1; } } else { this.field_70826_g = 0; } } return null; } /** * Checks to see if this enderman should be attacking this player */ private boolean shouldAttackPlayer(EntityPlayer par1EntityPlayer) { ItemStack var2 = par1EntityPlayer.inventory.armorInventory[3]; if (var2 != null && var2.itemID == Block.pumpkin.blockID) { return false; } else { Vec3 var3 = par1EntityPlayer.getLook(1.0F).normalize(); 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); double var5 = var4.lengthVector(); var4 = var4.normalize(); double var7 = var3.dotProduct(var4); return var7 > 1.0D - 0.025D / var5 ? par1EntityPlayer.canEntityBeSeen(this) : false; } } /** * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons * use this to react to sunlight and start to burn. */ public void onLivingUpdate() { if (this.isWet()) { this.attackEntityFrom(DamageSource.drown, 1); } this.moveSpeed = this.entityToAttack != null ? 6.5F : 0.3F; int var1; if (!this.worldObj.isRemote && this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")) { int var2; int var3; int var4; if (this.getCarried() == 0) { if (this.rand.nextInt(20) == 0) { var1 = MathHelper.floor_double(this.posX - 2.0D + this.rand.nextDouble() * 4.0D); var2 = MathHelper.floor_double(this.posY + this.rand.nextDouble() * 3.0D); var3 = MathHelper.floor_double(this.posZ - 2.0D + this.rand.nextDouble() * 4.0D); var4 = this.worldObj.getBlockId(var1, var2, var3); if (carriableBlocks[var4]) { this.setCarried(this.worldObj.getBlockId(var1, var2, var3)); this.setCarryingData(this.worldObj.getBlockMetadata(var1, var2, var3)); this.worldObj.setBlockWithNotify(var1, var2, var3, 0); } } } else if (this.rand.nextInt(2000) == 0) { var1 = MathHelper.floor_double(this.posX - 1.0D + this.rand.nextDouble() * 2.0D); var2 = MathHelper.floor_double(this.posY + this.rand.nextDouble() * 2.0D); var3 = MathHelper.floor_double(this.posZ - 1.0D + this.rand.nextDouble() * 2.0D); var4 = this.worldObj.getBlockId(var1, var2, var3); int var5 = this.worldObj.getBlockId(var1, var2 - 1, var3); if (var4 == 0 && var5 > 0 && Block.blocksList[var5].renderAsNormalBlock()) { this.worldObj.setBlockAndMetadataWithNotify(var1, var2, var3, this.getCarried(), this.getCarryingData()); this.setCarried(0); } } } for (var1 = 0; var1 < 2; ++var1) { 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); } if (this.worldObj.isDaytime() && !this.worldObj.isRemote) { float var6 = this.getBrightness(1.0F); 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) { this.entityToAttack = null; this.func_70819_e(false); this.teleportRandomly(); } } if (this.isWet() || this.isBurning()) { this.entityToAttack = null; this.func_70819_e(false); this.teleportRandomly(); } this.isJumping = false; if (this.entityToAttack != null) { this.faceEntity(this.entityToAttack, 100.0F, 100.0F); } if (!this.worldObj.isRemote && this.isEntityAlive()) { if (this.entityToAttack != null) { if (this.entityToAttack instanceof EntityPlayer && this.shouldAttackPlayer((EntityPlayer)this.entityToAttack)) { this.moveStrafing = this.moveForward = 0.0F; this.moveSpeed = 0.0F; if (this.entityToAttack.getDistanceSqToEntity(this) < 16.0D) { this.teleportRandomly(); } this.teleportDelay = 0; } else if (this.entityToAttack.getDistanceSqToEntity(this) > 256.0D && this.teleportDelay++ >= 30 && this.teleportToEntity(this.entityToAttack)) { this.teleportDelay = 0; } } else { this.func_70819_e(false); this.teleportDelay = 0; } } super.onLivingUpdate(); } /** * Teleport the enderman to a random nearby position */ protected boolean teleportRandomly() { double var1 = this.posX + (this.rand.nextDouble() - 0.5D) * 64.0D; double var3 = this.posY + (double)(this.rand.nextInt(64) - 32); double var5 = this.posZ + (this.rand.nextDouble() - 0.5D) * 64.0D; return this.teleportTo(var1, var3, var5); } /** * Teleport the enderman to another entity */ protected boolean teleportToEntity(Entity par1Entity) { 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); var2 = var2.normalize(); double var3 = 16.0D; double var5 = this.posX + (this.rand.nextDouble() - 0.5D) * 8.0D - var2.xCoord * var3; double var7 = this.posY + (double)(this.rand.nextInt(16) - 8) - var2.yCoord * var3; double var9 = this.posZ + (this.rand.nextDouble() - 0.5D) * 8.0D - var2.zCoord * var3; return this.teleportTo(var5, var7, var9); } /** * Teleport the enderman */ protected boolean teleportTo(double par1, double par3, double par5) { double var7 = this.posX; double var9 = this.posY; double var11 = this.posZ; this.posX = par1; this.posY = par3; this.posZ = par5; boolean var13 = false; int var14 = MathHelper.floor_double(this.posX); int var15 = MathHelper.floor_double(this.posY); int var16 = MathHelper.floor_double(this.posZ); int var18; if (this.worldObj.blockExists(var14, var15, var16)) { boolean var17 = false; while (!var17 && var15 > 0) { var18 = this.worldObj.getBlockId(var14, var15 - 1, var16); if (var18 != 0 && Block.blocksList[var18].blockMaterial.blocksMovement()) { var17 = true; } else { --this.posY; --var15; } } if (var17) { this.setPosition(this.posX, this.posY, this.posZ); if (this.worldObj.getCollidingBoundingBoxes(this, this.boundingBox).isEmpty() && !this.worldObj.isAnyLiquid(this.boundingBox)) { var13 = true; } } } if (!var13) { this.setPosition(var7, var9, var11); return false; } else { short var30 = 128; for (var18 = 0; var18 < var30; ++var18) { double var19 = (double)var18 / ((double)var30 - 1.0D); float var21 = (this.rand.nextFloat() - 0.5F) * 0.2F; float var22 = (this.rand.nextFloat() - 0.5F) * 0.2F; float var23 = (this.rand.nextFloat() - 0.5F) * 0.2F; double var24 = var7 + (this.posX - var7) * var19 + (this.rand.nextDouble() - 0.5D) * (double)this.width * 2.0D; double var26 = var9 + (this.posY - var9) * var19 + this.rand.nextDouble() * (double)this.height; double var28 = var11 + (this.posZ - var11) * var19 + (this.rand.nextDouble() - 0.5D) * (double)this.width * 2.0D; this.worldObj.spawnParticle("portal", var24, var26, var28, (double)var21, (double)var22, (double)var23); } this.worldObj.playSoundEffect(var7, var9, var11, "mob.endermen.portal", 1.0F, 1.0F); this.playSound("mob.endermen.portal", 1.0F, 1.0F); return true; } } /** * Returns the sound this mob makes while it's alive. */ protected String getLivingSound() { return this.func_70823_r() ? "mob.endermen.scream" : "mob.endermen.idle"; } /** * Returns the sound this mob makes when it is hurt. */ protected String getHurtSound() { return "mob.endermen.hit"; } /** * Returns the sound this mob makes on death. */ protected String getDeathSound() { return "mob.endermen.death"; } /** * Returns the item ID for the item the mob drops on death. */ protected int getDropItemId() { return Item.enderPearl.itemID; } /** * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param * par2 - Level of Looting used to kill this mob. */ protected void dropFewItems(boolean par1, int par2) { int var3 = this.getDropItemId(); if (var3 > 0) { int var4 = this.rand.nextInt(2 + par2); for (int var5 = 0; var5 < var4; ++var5) { this.dropItem(var3, 1); } } } /** * Set the id of the block an enderman carries */ public void setCarried(int par1) { this.dataWatcher.updateObject(16, Byte.valueOf((byte)(par1 & 255))); } /** * Get the id of the block an enderman carries */ public int getCarried() { return this.dataWatcher.getWatchableObjectByte(16); } /** * Set the metadata of the block an enderman carries */ public void setCarryingData(int par1) { this.dataWatcher.updateObject(17, Byte.valueOf((byte)(par1 & 255))); } /** * Get the metadata of the block an enderman carries */ public int getCarryingData() { return this.dataWatcher.getWatchableObjectByte(17); } /** * Called when the entity is attacked. */ public boolean attackEntityFrom(DamageSource par1DamageSource, int par2) { if (this.isEntityInvulnerable()) { return false; } else { this.func_70819_e(true); if (par1DamageSource instanceof EntityDamageSourceIndirect) { for (int var3 = 0; var3 < 64; ++var3) { if (this.teleportRandomly()) { return true; } } return false; } else { return super.attackEntityFrom(par1DamageSource, par2); } } } public boolean func_70823_r() { return this.dataWatcher.getWatchableObjectByte(18) > 0; } public void func_70819_e(boolean par1) { this.dataWatcher.updateObject(18, Byte.valueOf((byte)(par1 ? 1 : 0))); } /** * Returns the amount of damage a mob should deal. */ public int getAttackStrength(Entity par1Entity) { return 7; } static { carriableBlocks[Block.grass.blockID] = true; carriableBlocks[Block.dirt.blockID] = true; carriableBlocks[Block.sand.blockID] = true; carriableBlocks[Block.gravel.blockID] = true; carriableBlocks[Block.plantYellow.blockID] = true; carriableBlocks[Block.plantRed.blockID] = true; carriableBlocks[Block.mushroomBrown.blockID] = true; carriableBlocks[Block.mushroomRed.blockID] = true; carriableBlocks[Block.tnt.blockID] = true; carriableBlocks[Block.cactus.blockID] = true; carriableBlocks[Block.blockClay.blockID] = true; carriableBlocks[Block.pumpkin.blockID] = true; carriableBlocks[Block.melon.blockID] = true; carriableBlocks[Block.mycelium.blockID] = true; } }