001    package net.minecraft.src;
002    
003    import cpw.mods.fml.common.Side;
004    import cpw.mods.fml.common.asm.SideOnly;
005    import java.util.Calendar;
006    
007    public class EntityZombie extends EntityMob
008    {
009        private int field_82234_d = 0;
010    
011        public EntityZombie(World par1World)
012        {
013            super(par1World);
014            this.texture = "/mob/zombie.png";
015            this.moveSpeed = 0.23F;
016            this.getNavigator().setBreakDoors(true);
017            this.tasks.addTask(0, new EntityAISwimming(this));
018            this.tasks.addTask(1, new EntityAIBreakDoor(this));
019            this.tasks.addTask(2, new EntityAIAttackOnCollide(this, EntityPlayer.class, this.moveSpeed, false));
020            this.tasks.addTask(3, new EntityAIAttackOnCollide(this, EntityVillager.class, this.moveSpeed, true));
021            this.tasks.addTask(4, new EntityAIMoveTwardsRestriction(this, this.moveSpeed));
022            this.tasks.addTask(5, new EntityAIMoveThroughVillage(this, this.moveSpeed, false));
023            this.tasks.addTask(6, new EntityAIWander(this, this.moveSpeed));
024            this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
025            this.tasks.addTask(7, new EntityAILookIdle(this));
026            this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
027            this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, 16.0F, 0, true));
028            this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityVillager.class, 16.0F, 0, false));
029        }
030    
031        /**
032         * This method returns a value to be applied directly to entity speed, this factor is less than 1 when a slowdown
033         * potion effect is applied, more than 1 when a haste potion effect is applied and 2 for fleeing entities.
034         */
035        public float getSpeedModifier()
036        {
037            return super.getSpeedModifier() * (this.isChild() ? 1.5F : 1.0F);
038        }
039    
040        protected void entityInit()
041        {
042            super.entityInit();
043            this.getDataWatcher().addObject(12, Byte.valueOf((byte)0));
044            this.getDataWatcher().addObject(13, Byte.valueOf((byte)0));
045            this.getDataWatcher().addObject(14, Byte.valueOf((byte)0));
046        }
047    
048        @SideOnly(Side.CLIENT)
049    
050        /**
051         * Returns the texture's file path as a String.
052         */
053        public String getTexture()
054        {
055            return this.func_82231_m() ? "/mob/zombie_villager.png" : "/mob/zombie.png";
056        }
057    
058        public int getMaxHealth()
059        {
060            return 20;
061        }
062    
063        /**
064         * Returns the current armor value as determined by a call to InventoryPlayer.getTotalArmorValue
065         */
066        public int getTotalArmorValue()
067        {
068            int var1 = super.getTotalArmorValue() + 2;
069    
070            if (var1 > 20)
071            {
072                var1 = 20;
073            }
074    
075            return var1;
076        }
077    
078        /**
079         * Returns true if the newer Entity AI code should be run
080         */
081        protected boolean isAIEnabled()
082        {
083            return true;
084        }
085    
086        /**
087         * If Animal, checks if the age timer is negative
088         */
089        public boolean isChild()
090        {
091            return this.getDataWatcher().getWatchableObjectByte(12) == 1;
092        }
093    
094        public void func_82227_f(boolean par1)
095        {
096            this.getDataWatcher().updateObject(12, Byte.valueOf((byte)1));
097        }
098    
099        public boolean func_82231_m()
100        {
101            return this.getDataWatcher().getWatchableObjectByte(13) == 1;
102        }
103    
104        public void func_82229_g(boolean par1)
105        {
106            this.getDataWatcher().updateObject(13, Byte.valueOf((byte)(par1 ? 1 : 0)));
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.worldObj.isDaytime() && !this.worldObj.isRemote && !this.isChild())
116            {
117                float var1 = this.getBrightness(1.0F);
118    
119                if (var1 > 0.5F && this.rand.nextFloat() * 30.0F < (var1 - 0.4F) * 2.0F && this.worldObj.canBlockSeeTheSky(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)))
120                {
121                    boolean var2 = true;
122                    ItemStack var3 = this.getCurrentItemOrArmor(4);
123    
124                    if (var3 != null)
125                    {
126                        if (var3.isItemStackDamageable())
127                        {
128                            var3.setItemDamage(var3.getItemDamageForDisplay() + this.rand.nextInt(2));
129    
130                            if (var3.getItemDamageForDisplay() >= var3.getMaxDamage())
131                            {
132                                this.renderBrokenItemStack(var3);
133                                this.func_70062_b(4, (ItemStack)null);
134                            }
135                        }
136    
137                        var2 = false;
138                    }
139    
140                    if (var2)
141                    {
142                        this.setFire(8);
143                    }
144                }
145            }
146    
147            super.onLivingUpdate();
148        }
149    
150        /**
151         * Called to update the entity's position/logic.
152         */
153        public void onUpdate()
154        {
155            if (!this.worldObj.isRemote && this.func_82230_o())
156            {
157                int var1 = this.func_82233_q();
158                this.field_82234_d -= var1;
159    
160                if (this.field_82234_d <= 0)
161                {
162                    this.func_82232_p();
163                }
164            }
165    
166            super.onUpdate();
167        }
168    
169        public int func_82193_c(Entity par1Entity)
170        {
171            ItemStack var2 = this.getHeldItem();
172            int var3 = 4;
173    
174            if (var2 != null)
175            {
176                var3 += var2.getDamageVsEntity(this);
177            }
178    
179            return var3;
180        }
181    
182        /**
183         * Returns the sound this mob makes while it's alive.
184         */
185        protected String getLivingSound()
186        {
187            return "mob.zombie.say";
188        }
189    
190        /**
191         * Returns the sound this mob makes when it is hurt.
192         */
193        protected String getHurtSound()
194        {
195            return "mob.zombie.hurt";
196        }
197    
198        /**
199         * Returns the sound this mob makes on death.
200         */
201        protected String getDeathSound()
202        {
203            return "mob.zombie.death";
204        }
205    
206        /**
207         * Plays step sound at given x, y, z for the entity
208         */
209        protected void playStepSound(int par1, int par2, int par3, int par4)
210        {
211            this.worldObj.playSoundAtEntity(this, "mob.zombie.step", 0.15F, 1.0F);
212        }
213    
214        /**
215         * Returns the item ID for the item the mob drops on death.
216         */
217        protected int getDropItemId()
218        {
219            return Item.rottenFlesh.shiftedIndex;
220        }
221    
222        /**
223         * Get this Entity's EnumCreatureAttribute
224         */
225        public EnumCreatureAttribute getCreatureAttribute()
226        {
227            return EnumCreatureAttribute.UNDEAD;
228        }
229    
230        protected void dropRareDrop(int par1)
231        {
232            switch (this.rand.nextInt(3))
233            {
234                case 0:
235                    this.dropItem(Item.ingotIron.shiftedIndex, 1);
236                    break;
237                case 1:
238                    this.dropItem(Item.field_82797_bK.shiftedIndex, 1);
239                    break;
240                case 2:
241                    this.dropItem(Item.field_82794_bL.shiftedIndex, 1);
242            }
243        }
244    
245        protected void func_82164_bB()
246        {
247            super.func_82164_bB();
248    
249            if (this.rand.nextFloat() < (this.worldObj.difficultySetting == 3 ? 0.05F : 0.01F))
250            {
251                int var1 = this.rand.nextInt(3);
252    
253                if (var1 == 0)
254                {
255                    this.func_70062_b(0, new ItemStack(Item.swordSteel));
256                }
257                else
258                {
259                    this.func_70062_b(0, new ItemStack(Item.shovelSteel));
260                }
261            }
262        }
263    
264        /**
265         * (abstract) Protected helper method to write subclass entity data to NBT.
266         */
267        public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
268        {
269            super.writeEntityToNBT(par1NBTTagCompound);
270    
271            if (this.isChild())
272            {
273                par1NBTTagCompound.setBoolean("IsBaby", true);
274            }
275    
276            if (this.func_82231_m())
277            {
278                par1NBTTagCompound.setBoolean("IsVillager", true);
279            }
280    
281            par1NBTTagCompound.setInteger("ConversionTime", this.func_82230_o() ? this.field_82234_d : -1);
282        }
283    
284        /**
285         * (abstract) Protected helper method to read subclass entity data from NBT.
286         */
287        public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
288        {
289            super.readEntityFromNBT(par1NBTTagCompound);
290    
291            if (par1NBTTagCompound.getBoolean("IsBaby"))
292            {
293                this.func_82227_f(true);
294            }
295    
296            if (par1NBTTagCompound.getBoolean("IsVillager"))
297            {
298                this.func_82229_g(true);
299            }
300    
301            if (par1NBTTagCompound.hasKey("ConversionTime") && par1NBTTagCompound.getInteger("ConversionTime") > -1)
302            {
303                this.func_82228_a(par1NBTTagCompound.getInteger("ConversionTime"));
304            }
305        }
306    
307        /**
308         * This method gets called when the entity kills another one.
309         */
310        public void onKillEntity(EntityLiving par1EntityLiving)
311        {
312            super.onKillEntity(par1EntityLiving);
313    
314            if (this.worldObj.difficultySetting >= 2 && par1EntityLiving instanceof EntityVillager)
315            {
316                if (this.worldObj.difficultySetting == 2 && this.rand.nextBoolean())
317                {
318                    return;
319                }
320    
321                EntityZombie var2 = new EntityZombie(this.worldObj);
322                var2.func_82149_j(par1EntityLiving);
323                this.worldObj.setEntityDead(par1EntityLiving);
324                var2.func_82163_bD();
325                var2.func_82229_g(true);
326    
327                if (par1EntityLiving.isChild())
328                {
329                    var2.func_82227_f(true);
330                }
331    
332                this.worldObj.spawnEntityInWorld(var2);
333                this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1016, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
334            }
335        }
336    
337        public void func_82163_bD()
338        {
339            this.field_82172_bs = this.rand.nextFloat() < field_82181_as[this.worldObj.difficultySetting];
340    
341            if (this.worldObj.rand.nextFloat() < 0.05F)
342            {
343                this.func_82229_g(true);
344            }
345    
346            this.func_82164_bB();
347            this.func_82162_bC();
348    
349            if (this.getCurrentItemOrArmor(4) == null)
350            {
351                Calendar var1 = this.worldObj.func_83015_S();
352    
353                if (var1.get(2) + 1 == 10 && var1.get(5) == 31 && this.rand.nextFloat() < 0.25F)
354                {
355                    this.func_70062_b(4, new ItemStack(this.rand.nextFloat() < 0.1F ? Block.pumpkinLantern : Block.pumpkin));
356                    this.field_82174_bp[4] = 0.0F;
357                }
358            }
359        }
360    
361        /**
362         * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
363         */
364        public boolean interact(EntityPlayer par1EntityPlayer)
365        {
366            ItemStack var2 = par1EntityPlayer.getCurrentEquippedItem();
367    
368            if (var2 != null && var2.getItem() == Item.appleGold && var2.getItemDamage() == 0 && this.func_82231_m() && this.isPotionActive(Potion.weakness))
369            {
370                if (!par1EntityPlayer.capabilities.isCreativeMode)
371                {
372                    --var2.stackSize;
373                }
374    
375                if (var2.stackSize <= 0)
376                {
377                    par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
378                }
379    
380                if (!this.worldObj.isRemote)
381                {
382                    this.func_82228_a(this.rand.nextInt(2401) + 3600);
383                }
384    
385                return true;
386            }
387            else
388            {
389                return false;
390            }
391        }
392    
393        protected void func_82228_a(int par1)
394        {
395            this.field_82234_d = par1;
396            this.getDataWatcher().updateObject(14, Byte.valueOf((byte)1));
397            this.func_82170_o(Potion.weakness.id);
398            this.addPotionEffect(new PotionEffect(Potion.damageBoost.id, par1, Math.min(this.worldObj.difficultySetting - 1, 0)));
399            this.worldObj.setEntityState(this, (byte)16);
400        }
401    
402        @SideOnly(Side.CLIENT)
403        public void handleHealthUpdate(byte par1)
404        {
405            if (par1 == 16)
406            {
407                this.worldObj.playSound(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "mob.zombie.remedy", 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F);
408            }
409            else
410            {
411                super.handleHealthUpdate(par1);
412            }
413        }
414    
415        public boolean func_82230_o()
416        {
417            return this.getDataWatcher().getWatchableObjectByte(14) == 1;
418        }
419    
420        protected void func_82232_p()
421        {
422            EntityVillager var1 = new EntityVillager(this.worldObj);
423            var1.func_82149_j(this);
424            var1.func_82163_bD();
425            var1.func_82187_q();
426    
427            if (this.isChild())
428            {
429                var1.setGrowingAge(-24000);
430            }
431    
432            this.worldObj.setEntityDead(this);
433            this.worldObj.spawnEntityInWorld(var1);
434            var1.addPotionEffect(new PotionEffect(Potion.confusion.id, 200, 0));
435            this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1017, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
436        }
437    
438        protected int func_82233_q()
439        {
440            int var1 = 1;
441    
442            if (this.rand.nextFloat() < 0.01F)
443            {
444                int var2 = 0;
445    
446                for (int var3 = (int)this.posX - 4; var3 < (int)this.posX + 4 && var2 < 14; ++var3)
447                {
448                    for (int var4 = (int)this.posY - 4; var4 < (int)this.posY + 4 && var2 < 14; ++var4)
449                    {
450                        for (int var5 = (int)this.posZ - 4; var5 < (int)this.posZ + 4 && var2 < 14; ++var5)
451                        {
452                            int var6 = this.worldObj.getBlockId(var3, var4, var5);
453    
454                            if (var6 == Block.fenceIron.blockID || var6 == Block.bed.blockID)
455                            {
456                                if (this.rand.nextFloat() < 0.3F)
457                                {
458                                    ++var1;
459                                }
460    
461                                ++var2;
462                            }
463                        }
464                    }
465                }
466            }
467    
468            return var1;
469        }
470    }