    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 setIsVillager(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.func_85030_a("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.shiftedIndex;
    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.shiftedIndex, 1);
    274                    break;
    275                case 1:
    276                    this.dropItem(Item.carrot.shiftedIndex, 1);
    277                    break;
    278                case 2:
    279                    this.dropItem(Item.potato.shiftedIndex, 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.setIsVillager(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.setIsVillager(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.setIsVillager(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    }