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