    001package net.minecraft.entity.monster;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.util.Calendar;
    006import net.minecraft.block.Block;
    007import net.minecraft.entity.Entity;
    008import net.minecraft.entity.EntityLiving;
    009import net.minecraft.entity.EnumCreatureAttribute;
    010import net.minecraft.entity.ai.EntityAIAttackOnCollide;
    011import net.minecraft.entity.ai.EntityAIBreakDoor;
    012import net.minecraft.entity.ai.EntityAIHurtByTarget;
    013import net.minecraft.entity.ai.EntityAILookIdle;
    014import net.minecraft.entity.ai.EntityAIMoveThroughVillage;
    015import net.minecraft.entity.ai.EntityAIMoveTwardsRestriction;
    016import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
    017import net.minecraft.entity.ai.EntityAISwimming;
    018import net.minecraft.entity.ai.EntityAIWander;
    019import net.minecraft.entity.ai.EntityAIWatchClosest;
    020import net.minecraft.entity.passive.EntityVillager;
    021import net.minecraft.entity.player.EntityPlayer;
    022import net.minecraft.item.Item;
    023import net.minecraft.item.ItemStack;
    024import net.minecraft.nbt.NBTTagCompound;
    025import net.minecraft.potion.Potion;
    026import net.minecraft.potion.PotionEffect;
    027import net.minecraft.util.MathHelper;
    028import net.minecraft.world.World;
    029
    030public 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.isConverting())
    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.isConverting() ? 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.removeEntity(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    /**
    461     * Returns whether this zombie is in the process of converting to a villager
    462     */
    463    public boolean isConverting()
    464    {
    465        return this.getDataWatcher().getWatchableObjectByte(14) == 1;
    466    }
    467
    468    /**
    469     * Convert this zombie into a villager.
    470     */
    471    protected void convertToVillager()
    472    {
    473        EntityVillager var1 = new EntityVillager(this.worldObj);
    474        var1.func_82149_j(this);
    475        var1.initCreature();
    476        var1.func_82187_q();
    477
    478        if (this.isChild())
    479        {
    480            var1.setGrowingAge(-24000);
    481        }
    482
    483        this.worldObj.removeEntity(this);
    484        this.worldObj.spawnEntityInWorld(var1);
    485        var1.addPotionEffect(new PotionEffect(Potion.confusion.id, 200, 0));
    486        this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1017, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
    487    }
    488
    489    /**
    490     * Return the amount of time decremented from conversionTime every tick.
    491     */
    492    protected int getConversionTimeBoost()
    493    {
    494        int var1 = 1;
    495
    496        if (this.rand.nextFloat() < 0.01F)
    497        {
    498            int var2 = 0;
    499
    500            for (int var3 = (int)this.posX - 4; var3 < (int)this.posX + 4 && var2 < 14; ++var3)
    501            {
    502                for (int var4 = (int)this.posY - 4; var4 < (int)this.posY + 4 && var2 < 14; ++var4)
    503                {
    504                    for (int var5 = (int)this.posZ - 4; var5 < (int)this.posZ + 4 && var2 < 14; ++var5)
    505                    {
    506                        int var6 = this.worldObj.getBlockId(var3, var4, var5);
    507
    508                        if (var6 == Block.fenceIron.blockID || var6 == Block.bed.blockID)
    509                        {
    510                            if (this.rand.nextFloat() < 0.3F)
    511                            {
    512                                ++var1;
    513                            }
    514
    515                            ++var2;
    516                        }
    517                    }
    518                }
    519            }
    520        }
    521
    522        return var1;
    523    }
    524}