    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    }