    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 setVillager(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.worldObj.playSoundAtEntity(this, "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.potatoe.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.setVillager(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.setVillager(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.setVillager(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    }