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