    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    }