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