    001    package net.minecraft.entity.monster;
    002
    003    import net.minecraft.block.Block;
    004    import net.minecraft.entity.Entity;
    005    import net.minecraft.entity.player.EntityPlayer;
    006    import net.minecraft.item.Item;
    007    import net.minecraft.item.ItemStack;
    008    import net.minecraft.nbt.NBTTagCompound;
    009    import net.minecraft.util.DamageSource;
    010    import net.minecraft.util.EntityDamageSourceIndirect;
    011    import net.minecraft.util.MathHelper;
    012    import net.minecraft.util.Vec3;
    013    import net.minecraft.world.World;
    014
    015    public class EntityEnderman extends EntityMob
    016    {
    017        public static boolean[] carriableBlocks = new boolean[256];
    018
    019        /**
    020         * Counter to delay the teleportation of an enderman towards the currently attacked target
    021         */
    022        private int teleportDelay = 0;
    023        private int field_70826_g = 0;
    024
    025        public EntityEnderman(World par1World)
    026        {
    027            super(par1World);
    028            this.texture = "/mob/enderman.png";
    029            this.moveSpeed = 0.2F;
    030            this.setSize(0.6F, 2.9F);
    031            this.stepHeight = 1.0F;
    032        }
    033
    034        public int getMaxHealth()
    035        {
    036            return 40;
    037        }
    038
    039        protected void entityInit()
    040        {
    041            super.entityInit();
    042            this.dataWatcher.addObject(16, new Byte((byte)0));
    043            this.dataWatcher.addObject(17, new Byte((byte)0));
    044            this.dataWatcher.addObject(18, new Byte((byte)0));
    045        }
    046
    047        /**
    048         * (abstract) Protected helper method to write subclass entity data to NBT.
    049         */
    050        public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    051        {
    052            super.writeEntityToNBT(par1NBTTagCompound);
    053            par1NBTTagCompound.setShort("carried", (short)this.getCarried());
    054            par1NBTTagCompound.setShort("carriedData", (short)this.getCarryingData());
    055        }
    056
    057        /**
    058         * (abstract) Protected helper method to read subclass entity data from NBT.
    059         */
    060        public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    061        {
    062            super.readEntityFromNBT(par1NBTTagCompound);
    063            this.setCarried(par1NBTTagCompound.getShort("carried"));
    064            this.setCarryingData(par1NBTTagCompound.getShort("carriedData"));
    065        }
    066
    067        /**
    068         * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking
    069         * (Animals, Spiders at day, peaceful PigZombies).
    070         */
    071        protected Entity findPlayerToAttack()
    072        {
    073            EntityPlayer var1 = this.worldObj.getClosestVulnerablePlayerToEntity(this, 64.0D);
    074
    075            if (var1 != null)
    076            {
    077                if (this.shouldAttackPlayer(var1))
    078                {
    079                    if (this.field_70826_g == 0)
    080                    {
    081                        this.worldObj.playSoundAtEntity(var1, "mob.endermen.stare", 1.0F, 1.0F);
    082                    }
    083
    084                    if (this.field_70826_g++ == 5)
    085                    {
    086                        this.field_70826_g = 0;
    087                        this.func_70819_e(true);
    088                        return var1;
    089                    }
    090                }
    091                else
    092                {
    093                    this.field_70826_g = 0;
    094                }
    095            }
    096
    097            return null;
    098        }
    099
    100        /**
    101         * Checks to see if this enderman should be attacking this player
    102         */
    103        private boolean shouldAttackPlayer(EntityPlayer par1EntityPlayer)
    104        {
    105            ItemStack var2 = par1EntityPlayer.inventory.armorInventory[3];
    106
    107            if (var2 != null && var2.itemID == Block.pumpkin.blockID)
    108            {
    109                return false;
    110            }
    111            else
    112            {
    113                Vec3 var3 = par1EntityPlayer.getLook(1.0F).normalize();
    114                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);
    115                double var5 = var4.lengthVector();
    116                var4 = var4.normalize();
    117                double var7 = var3.dotProduct(var4);
    118                return var7 > 1.0D - 0.025D / var5 ? par1EntityPlayer.canEntityBeSeen(this) : false;
    119            }
    120        }
    121
    122        /**
    123         * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
    124         * use this to react to sunlight and start to burn.
    125         */
    126        public void onLivingUpdate()
    127        {
    128            if (this.isWet())
    129            {
    130                this.attackEntityFrom(DamageSource.drown, 1);
    131            }
    132
    133            this.moveSpeed = this.entityToAttack != null ? 6.5F : 0.3F;
    134            int var1;
    135
    136            if (!this.worldObj.isRemote && this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing"))
    137            {
    138                int var2;
    139                int var3;
    140                int var4;
    141
    142                if (this.getCarried() == 0)
    143                {
    144                    if (this.rand.nextInt(20) == 0)
    145                    {
    146                        var1 = MathHelper.floor_double(this.posX - 2.0D + this.rand.nextDouble() * 4.0D);
    147                        var2 = MathHelper.floor_double(this.posY + this.rand.nextDouble() * 3.0D);
    148                        var3 = MathHelper.floor_double(this.posZ - 2.0D + this.rand.nextDouble() * 4.0D);
    149                        var4 = this.worldObj.getBlockId(var1, var2, var3);
    150
    151                        if (carriableBlocks[var4])
    152                        {
    153                            this.setCarried(this.worldObj.getBlockId(var1, var2, var3));
    154                            this.setCarryingData(this.worldObj.getBlockMetadata(var1, var2, var3));
    155                            this.worldObj.setBlockWithNotify(var1, var2, var3, 0);
    156                        }
    157                    }
    158                }
    159                else if (this.rand.nextInt(2000) == 0)
    160                {
    161                    var1 = MathHelper.floor_double(this.posX - 1.0D + this.rand.nextDouble() * 2.0D);
    162                    var2 = MathHelper.floor_double(this.posY + this.rand.nextDouble() * 2.0D);
    163                    var3 = MathHelper.floor_double(this.posZ - 1.0D + this.rand.nextDouble() * 2.0D);
    164                    var4 = this.worldObj.getBlockId(var1, var2, var3);
    165                    int var5 = this.worldObj.getBlockId(var1, var2 - 1, var3);
    166
    167                    if (var4 == 0 && var5 > 0 && Block.blocksList[var5].renderAsNormalBlock())
    168                    {
    169                        this.worldObj.setBlockAndMetadataWithNotify(var1, var2, var3, this.getCarried(), this.getCarryingData());
    170                        this.setCarried(0);
    171                    }
    172                }
    173            }
    174
    175            for (var1 = 0; var1 < 2; ++var1)
    176            {
    177                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);
    178            }
    179
    180            if (this.worldObj.isDaytime() && !this.worldObj.isRemote)
    181            {
    182                float var6 = this.getBrightness(1.0F);
    183
    184                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)
    185                {
    186                    this.entityToAttack = null;
    187                    this.func_70819_e(false);
    188                    this.teleportRandomly();
    189                }
    190            }
    191
    192            if (this.isWet())
    193            {
    194                this.entityToAttack = null;
    195                this.func_70819_e(false);
    196                this.teleportRandomly();
    197            }
    198
    199            this.isJumping = false;
    200
    201            if (this.entityToAttack != null)
    202            {
    203                this.faceEntity(this.entityToAttack, 100.0F, 100.0F);
    204            }
    205
    206            if (!this.worldObj.isRemote && this.isEntityAlive())
    207            {
    208                if (this.entityToAttack != null)
    209                {
    210                    if (this.entityToAttack instanceof EntityPlayer && this.shouldAttackPlayer((EntityPlayer)this.entityToAttack))
    211                    {
    212                        this.moveStrafing = this.moveForward = 0.0F;
    213                        this.moveSpeed = 0.0F;
    214
    215                        if (this.entityToAttack.getDistanceSqToEntity(this) < 16.0D)
    216                        {
    217                            this.teleportRandomly();
    218                        }
    219
    220                        this.teleportDelay = 0;
    221                    }
    222                    else if (this.entityToAttack.getDistanceSqToEntity(this) > 256.0D && this.teleportDelay++ >= 30 && this.teleportToEntity(this.entityToAttack))
    223                    {
    224                        this.teleportDelay = 0;
    225                    }
    226                }
    227                else
    228                {
    229                    this.func_70819_e(false);
    230                    this.teleportDelay = 0;
    231                }
    232            }
    233
    234            super.onLivingUpdate();
    235        }
    236
    237        /**
    238         * Teleport the enderman to a random nearby position
    239         */
    240        protected boolean teleportRandomly()
    241        {
    242            double var1 = this.posX + (this.rand.nextDouble() - 0.5D) * 64.0D;
    243            double var3 = this.posY + (double)(this.rand.nextInt(64) - 32);
    244            double var5 = this.posZ + (this.rand.nextDouble() - 0.5D) * 64.0D;
    245            return this.teleportTo(var1, var3, var5);
    246        }
    247
    248        /**
    249         * Teleport the enderman to another entity
    250         */
    251        protected boolean teleportToEntity(Entity par1Entity)
    252        {
    253            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);
    254            var2 = var2.normalize();
    255            double var3 = 16.0D;
    256            double var5 = this.posX + (this.rand.nextDouble() - 0.5D) * 8.0D - var2.xCoord * var3;
    257            double var7 = this.posY + (double)(this.rand.nextInt(16) - 8) - var2.yCoord * var3;
    258            double var9 = this.posZ + (this.rand.nextDouble() - 0.5D) * 8.0D - var2.zCoord * var3;
    259            return this.teleportTo(var5, var7, var9);
    260        }
    261
    262        /**
    263         * Teleport the enderman
    264         */
    265        protected boolean teleportTo(double par1, double par3, double par5)
    266        {
    267            double var7 = this.posX;
    268            double var9 = this.posY;
    269            double var11 = this.posZ;
    270            this.posX = par1;
    271            this.posY = par3;
    272            this.posZ = par5;
    273            boolean var13 = false;
    274            int var14 = MathHelper.floor_double(this.posX);
    275            int var15 = MathHelper.floor_double(this.posY);
    276            int var16 = MathHelper.floor_double(this.posZ);
    277            int var18;
    278
    279            if (this.worldObj.blockExists(var14, var15, var16))
    280            {
    281                boolean var17 = false;
    282
    283                while (!var17 && var15 > 0)
    284                {
    285                    var18 = this.worldObj.getBlockId(var14, var15 - 1, var16);
    286
    287                    if (var18 != 0 && Block.blocksList[var18].blockMaterial.blocksMovement())
    288                    {
    289                        var17 = true;
    290                    }
    291                    else
    292                    {
    293                        --this.posY;
    294                        --var15;
    295                    }
    296                }
    297
    298                if (var17)
    299                {
    300                    this.setPosition(this.posX, this.posY, this.posZ);
    301
    302                    if (this.worldObj.getCollidingBoundingBoxes(this, this.boundingBox).isEmpty() && !this.worldObj.isAnyLiquid(this.boundingBox))
    303                    {
    304                        var13 = true;
    305                    }
    306                }
    307            }
    308
    309            if (!var13)
    310            {
    311                this.setPosition(var7, var9, var11);
    312                return false;
    313            }
    314            else
    315            {
    316                short var30 = 128;
    317
    318                for (var18 = 0; var18 < var30; ++var18)
    319                {
    320                    double var19 = (double)var18 / ((double)var30 - 1.0D);
    321                    float var21 = (this.rand.nextFloat() - 0.5F) * 0.2F;
    322                    float var22 = (this.rand.nextFloat() - 0.5F) * 0.2F;
    323                    float var23 = (this.rand.nextFloat() - 0.5F) * 0.2F;
    324                    double var24 = var7 + (this.posX - var7) * var19 + (this.rand.nextDouble() - 0.5D) * (double)this.width * 2.0D;
    325                    double var26 = var9 + (this.posY - var9) * var19 + this.rand.nextDouble() * (double)this.height;
    326                    double var28 = var11 + (this.posZ - var11) * var19 + (this.rand.nextDouble() - 0.5D) * (double)this.width * 2.0D;
    327                    this.worldObj.spawnParticle("portal", var24, var26, var28, (double)var21, (double)var22, (double)var23);
    328                }
    329
    330                this.worldObj.playSoundEffect(var7, var9, var11, "mob.endermen.portal", 1.0F, 1.0F);
    331                this.func_85030_a("mob.endermen.portal", 1.0F, 1.0F);
    332                return true;
    333            }
    334        }
    335
    336        /**
    337         * Returns the sound this mob makes while it's alive.
    338         */
    339        protected String getLivingSound()
    340        {
    341            return this.func_70823_r() ? "mob.endermen.scream" : "mob.endermen.idle";
    342        }
    343
    344        /**
    345         * Returns the sound this mob makes when it is hurt.
    346         */
    347        protected String getHurtSound()
    348        {
    349            return "mob.endermen.hit";
    350        }
    351
    352        /**
    353         * Returns the sound this mob makes on death.
    354         */
    355        protected String getDeathSound()
    356        {
    357            return "mob.endermen.death";
    358        }
    359
    360        /**
    361         * Returns the item ID for the item the mob drops on death.
    362         */
    363        protected int getDropItemId()
    364        {
    365            return Item.enderPearl.shiftedIndex;
    366        }
    367
    368        /**
    369         * Drop 0-2 items of this living's type
    370         */
    371        protected void dropFewItems(boolean par1, int par2)
    372        {
    373            int var3 = this.getDropItemId();
    374
    375            if (var3 > 0)
    376            {
    377                int var4 = this.rand.nextInt(2 + par2);
    378
    379                for (int var5 = 0; var5 < var4; ++var5)
    380                {
    381                    this.dropItem(var3, 1);
    382                }
    383            }
    384        }
    385
    386        /**
    387         * Set the id of the block an enderman carries
    388         */
    389        public void setCarried(int par1)
    390        {
    391            this.dataWatcher.updateObject(16, Byte.valueOf((byte)(par1 & 255)));
    392        }
    393
    394        /**
    395         * Get the id of the block an enderman carries
    396         */
    397        public int getCarried()
    398        {
    399            return this.dataWatcher.getWatchableObjectByte(16);
    400        }
    401
    402        /**
    403         * Set the metadata of the block an enderman carries
    404         */
    405        public void setCarryingData(int par1)
    406        {
    407            this.dataWatcher.updateObject(17, Byte.valueOf((byte)(par1 & 255)));
    408        }
    409
    410        /**
    411         * Get the metadata of the block an enderman carries
    412         */
    413        public int getCarryingData()
    414        {
    415            return this.dataWatcher.getWatchableObjectByte(17);
    416        }
    417
    418        /**
    419         * Called when the entity is attacked.
    420         */
    421        public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
    422        {
    423            if (this.func_85032_ar())
    424            {
    425                return false;
    426            }
    427            else if (par1DamageSource instanceof EntityDamageSourceIndirect)
    428            {
    429                for (int var3 = 0; var3 < 64; ++var3)
    430                {
    431                    if (this.teleportRandomly())
    432                    {
    433                        return true;
    434                    }
    435                }
    436
    437                return false;
    438            }
    439            else
    440            {
    441                if (par1DamageSource.getEntity() instanceof EntityPlayer)
    442                {
    443                    this.func_70819_e(true);
    444                }
    445
    446                return super.attackEntityFrom(par1DamageSource, par2);
    447            }
    448        }
    449
    450        public boolean func_70823_r()
    451        {
    452            return this.dataWatcher.getWatchableObjectByte(18) > 0;
    453        }
    454
    455        public void func_70819_e(boolean par1)
    456        {
    457            this.dataWatcher.updateObject(18, Byte.valueOf((byte)(par1 ? 1 : 0)));
    458        }
    459
    460        /**
    461         * Returns the amount of damage a mob should deal.
    462         */
    463        public int getAttackStrength(Entity par1Entity)
    464        {
    465            return 7;
    466        }
    467
    468        static
    469        {
    470            carriableBlocks[Block.grass.blockID] = true;
    471            carriableBlocks[Block.dirt.blockID] = true;
    472            carriableBlocks[Block.sand.blockID] = true;
    473            carriableBlocks[Block.gravel.blockID] = true;
    474            carriableBlocks[Block.plantYellow.blockID] = true;
    475            carriableBlocks[Block.plantRed.blockID] = true;
    476            carriableBlocks[Block.mushroomBrown.blockID] = true;
    477            carriableBlocks[Block.mushroomRed.blockID] = true;
    478            carriableBlocks[Block.tnt.blockID] = true;
    479            carriableBlocks[Block.cactus.blockID] = true;
    480            carriableBlocks[Block.blockClay.blockID] = true;
    481            carriableBlocks[Block.pumpkin.blockID] = true;
    482            carriableBlocks[Block.melon.blockID] = true;
    483            carriableBlocks[Block.mycelium.blockID] = true;
    484        }
    485    }