    001package net.minecraft.entity.monster;
    002
    003import net.minecraft.block.Block;
    004import net.minecraft.entity.Entity;
    005import net.minecraft.entity.player.EntityPlayer;
    006import net.minecraft.item.Item;
    007import net.minecraft.item.ItemStack;
    008import net.minecraft.nbt.NBTTagCompound;
    009import net.minecraft.util.DamageSource;
    010import net.minecraft.util.EntityDamageSourceIndirect;
    011import net.minecraft.util.MathHelper;
    012import net.minecraft.util.Vec3;
    013import net.minecraft.world.World;
    014
    015public 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.setScreaming(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.setScreaming(false);
    188                this.teleportRandomly();
    189            }
    190        }
    191
    192        if (this.isWet() || this.isBurning())
    193        {
    194            this.entityToAttack = null;
    195            this.setScreaming(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.setScreaming(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.playSound("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.isScreaming() ? "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.itemID;
    366    }
    367
    368    /**
    369     * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param
    370     * par2 - Level of Looting used to kill this mob.
    371     */
    372    protected void dropFewItems(boolean par1, int par2)
    373    {
    374        int var3 = this.getDropItemId();
    375
    376        if (var3 > 0)
    377        {
    378            int var4 = this.rand.nextInt(2 + par2);
    379
    380            for (int var5 = 0; var5 < var4; ++var5)
    381            {
    382                this.dropItem(var3, 1);
    383            }
    384        }
    385    }
    386
    387    /**
    388     * Set the id of the block an enderman carries
    389     */
    390    public void setCarried(int par1)
    391    {
    392        this.dataWatcher.updateObject(16, Byte.valueOf((byte)(par1 & 255)));
    393    }
    394
    395    /**
    396     * Get the id of the block an enderman carries
    397     */
    398    public int getCarried()
    399    {
    400        return this.dataWatcher.getWatchableObjectByte(16);
    401    }
    402
    403    /**
    404     * Set the metadata of the block an enderman carries
    405     */
    406    public void setCarryingData(int par1)
    407    {
    408        this.dataWatcher.updateObject(17, Byte.valueOf((byte)(par1 & 255)));
    409    }
    410
    411    /**
    412     * Get the metadata of the block an enderman carries
    413     */
    414    public int getCarryingData()
    415    {
    416        return this.dataWatcher.getWatchableObjectByte(17);
    417    }
    418
    419    /**
    420     * Called when the entity is attacked.
    421     */
    422    public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
    423    {
    424        if (this.isEntityInvulnerable())
    425        {
    426            return false;
    427        }
    428        else
    429        {
    430            this.setScreaming(true);
    431
    432            if (par1DamageSource instanceof EntityDamageSourceIndirect)
    433            {
    434                for (int var3 = 0; var3 < 64; ++var3)
    435                {
    436                    if (this.teleportRandomly())
    437                    {
    438                        return true;
    439                    }
    440                }
    441
    442                return false;
    443            }
    444            else
    445            {
    446                return super.attackEntityFrom(par1DamageSource, par2);
    447            }
    448        }
    449    }
    450
    451    public boolean isScreaming()
    452    {
    453        return this.dataWatcher.getWatchableObjectByte(18) > 0;
    454    }
    455
    456    public void setScreaming(boolean par1)
    457    {
    458        this.dataWatcher.updateObject(18, Byte.valueOf((byte)(par1 ? 1 : 0)));
    459    }
    460
    461    /**
    462     * Returns the amount of damage a mob should deal.
    463     */
    464    public int getAttackStrength(Entity par1Entity)
    465    {
    466        return 7;
    467    }
    468
    469    static
    470    {
    471        carriableBlocks[Block.grass.blockID] = true;
    472        carriableBlocks[Block.dirt.blockID] = true;
    473        carriableBlocks[Block.sand.blockID] = true;
    474        carriableBlocks[Block.gravel.blockID] = true;
    475        carriableBlocks[Block.plantYellow.blockID] = true;
    476        carriableBlocks[Block.plantRed.blockID] = true;
    477        carriableBlocks[Block.mushroomBrown.blockID] = true;
    478        carriableBlocks[Block.mushroomRed.blockID] = true;
    479        carriableBlocks[Block.tnt.blockID] = true;
    480        carriableBlocks[Block.cactus.blockID] = true;
    481        carriableBlocks[Block.blockClay.blockID] = true;
    482        carriableBlocks[Block.pumpkin.blockID] = true;
    483        carriableBlocks[Block.melon.blockID] = true;
    484        carriableBlocks[Block.mycelium.blockID] = true;
    485    }
    486}