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