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