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 entityplayer = this.worldObj.getClosestVulnerablePlayerToEntity(this, 64.0D);
074
075        if (entityplayer != null)
076        {
077            if (this.shouldAttackPlayer(entityplayer))
078            {
079                if (this.field_70826_g == 0)
080                {
081                    this.worldObj.playSoundAtEntity(entityplayer, "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 entityplayer;
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 itemstack = par1EntityPlayer.inventory.armorInventory[3];
106
107        if (itemstack != null && itemstack.itemID == Block.pumpkin.blockID)
108        {
109            return false;
110        }
111        else
112        {
113            Vec3 vec3 = par1EntityPlayer.getLook(1.0F).normalize();
114            Vec3 vec31 = 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 d0 = vec31.lengthVector();
116            vec31 = vec31.normalize();
117            double d1 = vec3.dotProduct(vec31);
118            return d1 > 1.0D - 0.025D / d0 ? 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 i;
135
136        if (!this.worldObj.isRemote && this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing"))
137        {
138            int j;
139            int k;
140            int l;
141
142            if (this.getCarried() == 0)
143            {
144                if (this.rand.nextInt(20) == 0)
145                {
146                    i = MathHelper.floor_double(this.posX - 2.0D + this.rand.nextDouble() * 4.0D);
147                    j = MathHelper.floor_double(this.posY + this.rand.nextDouble() * 3.0D);
148                    k = MathHelper.floor_double(this.posZ - 2.0D + this.rand.nextDouble() * 4.0D);
149                    l = this.worldObj.getBlockId(i, j, k);
150
151                    if (carriableBlocks[l])
152                    {
153                        this.setCarried(this.worldObj.getBlockId(i, j, k));
154                        this.setCarryingData(this.worldObj.getBlockMetadata(i, j, k));
155                        this.worldObj.setBlock(i, j, k, 0);
156                    }
157                }
158            }
159            else if (this.rand.nextInt(2000) == 0)
160            {
161                i = MathHelper.floor_double(this.posX - 1.0D + this.rand.nextDouble() * 2.0D);
162                j = MathHelper.floor_double(this.posY + this.rand.nextDouble() * 2.0D);
163                k = MathHelper.floor_double(this.posZ - 1.0D + this.rand.nextDouble() * 2.0D);
164                l = this.worldObj.getBlockId(i, j, k);
165                int i1 = this.worldObj.getBlockId(i, j - 1, k);
166
167                if (l == 0 && i1 > 0 && Block.blocksList[i1].renderAsNormalBlock())
168                {
169                    this.worldObj.setBlock(i, j, k, this.getCarried(), this.getCarryingData(), 3);
170                    this.setCarried(0);
171                }
172            }
173        }
174
175        for (i = 0; i < 2; ++i)
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 f = this.getBrightness(1.0F);
183
184            if (f > 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 < (f - 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 d0 = this.posX + (this.rand.nextDouble() - 0.5D) * 64.0D;
243        double d1 = this.posY + (double)(this.rand.nextInt(64) - 32);
244        double d2 = this.posZ + (this.rand.nextDouble() - 0.5D) * 64.0D;
245        return this.teleportTo(d0, d1, d2);
246    }
247
248    /**
249     * Teleport the enderman to another entity
250     */
251    protected boolean teleportToEntity(Entity par1Entity)
252    {
253        Vec3 vec3 = 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        vec3 = vec3.normalize();
255        double d0 = 16.0D;
256        double d1 = this.posX + (this.rand.nextDouble() - 0.5D) * 8.0D - vec3.xCoord * d0;
257        double d2 = this.posY + (double)(this.rand.nextInt(16) - 8) - vec3.yCoord * d0;
258        double d3 = this.posZ + (this.rand.nextDouble() - 0.5D) * 8.0D - vec3.zCoord * d0;
259        return this.teleportTo(d1, d2, d3);
260    }
261
262    /**
263     * Teleport the enderman
264     */
265    protected boolean teleportTo(double par1, double par3, double par5)
266    {
267        double d3 = this.posX;
268        double d4 = this.posY;
269        double d5 = this.posZ;
270        this.posX = par1;
271        this.posY = par3;
272        this.posZ = par5;
273        boolean flag = false;
274        int i = MathHelper.floor_double(this.posX);
275        int j = MathHelper.floor_double(this.posY);
276        int k = MathHelper.floor_double(this.posZ);
277        int l;
278
279        if (this.worldObj.blockExists(i, j, k))
280        {
281            boolean flag1 = false;
282
283            while (!flag1 && j > 0)
284            {
285                l = this.worldObj.getBlockId(i, j - 1, k);
286
287                if (l != 0 && Block.blocksList[l].blockMaterial.blocksMovement())
288                {
289                    flag1 = true;
290                }
291                else
292                {
293                    --this.posY;
294                    --j;
295                }
296            }
297
298            if (flag1)
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                    flag = true;
305                }
306            }
307        }
308
309        if (!flag)
310        {
311            this.setPosition(d3, d4, d5);
312            return false;
313        }
314        else
315        {
316            short short1 = 128;
317
318            for (l = 0; l < short1; ++l)
319            {
320                double d6 = (double)l / ((double)short1 - 1.0D);
321                float f = (this.rand.nextFloat() - 0.5F) * 0.2F;
322                float f1 = (this.rand.nextFloat() - 0.5F) * 0.2F;
323                float f2 = (this.rand.nextFloat() - 0.5F) * 0.2F;
324                double d7 = d3 + (this.posX - d3) * d6 + (this.rand.nextDouble() - 0.5D) * (double)this.width * 2.0D;
325                double d8 = d4 + (this.posY - d4) * d6 + this.rand.nextDouble() * (double)this.height;
326                double d9 = d5 + (this.posZ - d5) * d6 + (this.rand.nextDouble() - 0.5D) * (double)this.width * 2.0D;
327                this.worldObj.spawnParticle("portal", d7, d8, d9, (double)f, (double)f1, (double)f2);
328            }
329
330            this.worldObj.playSoundEffect(d3, d4, d5, "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 j = this.getDropItemId();
375
376        if (j > 0)
377        {
378            int k = this.rand.nextInt(2 + par2);
379
380            for (int l = 0; l < k; ++l)
381            {
382                this.dropItem(j, 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 j = 0; j < 64; ++j)
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}