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() || this.isBurning())
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.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.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.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.func_70819_e(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 func_70823_r()
452        {
453            return this.dataWatcher.getWatchableObjectByte(18) > 0;
454        }
455    
456        public void func_70819_e(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    }