    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.enchantment.Enchantment;
    008import net.minecraft.enchantment.EnchantmentHelper;
    009import net.minecraft.entity.Entity;
    010import net.minecraft.entity.EntityLiving;
    011import net.minecraft.entity.EnumCreatureAttribute;
    012import net.minecraft.entity.IRangedAttackMob;
    013import net.minecraft.entity.ai.EntityAIArrowAttack;
    014import net.minecraft.entity.ai.EntityAIAttackOnCollide;
    015import net.minecraft.entity.ai.EntityAIFleeSun;
    016import net.minecraft.entity.ai.EntityAIHurtByTarget;
    017import net.minecraft.entity.ai.EntityAILookIdle;
    018import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
    019import net.minecraft.entity.ai.EntityAIRestrictSun;
    020import net.minecraft.entity.ai.EntityAISwimming;
    021import net.minecraft.entity.ai.EntityAIWander;
    022import net.minecraft.entity.ai.EntityAIWatchClosest;
    023import net.minecraft.entity.player.EntityPlayer;
    024import net.minecraft.entity.projectile.EntityArrow;
    025import net.minecraft.item.Item;
    026import net.minecraft.item.ItemStack;
    027import net.minecraft.nbt.NBTTagCompound;
    028import net.minecraft.potion.Potion;
    029import net.minecraft.potion.PotionEffect;
    030import net.minecraft.stats.AchievementList;
    031import net.minecraft.util.DamageSource;
    032import net.minecraft.util.MathHelper;
    033import net.minecraft.world.World;
    034import net.minecraft.world.WorldProviderHell;
    035
    036public class EntitySkeleton extends EntityMob implements IRangedAttackMob
    037{
    038    private EntityAIArrowAttack aiArrowAttack = new EntityAIArrowAttack(this, 0.25F, 60, 10.0F);
    039    private EntityAIAttackOnCollide aiAttackOnCollide = new EntityAIAttackOnCollide(this, EntityPlayer.class, 0.31F, false);
    040
    041    public EntitySkeleton(World par1World)
    042    {
    043        super(par1World);
    044        this.texture = "/mob/skeleton.png";
    045        this.moveSpeed = 0.25F;
    046        this.tasks.addTask(1, new EntityAISwimming(this));
    047        this.tasks.addTask(2, new EntityAIRestrictSun(this));
    048        this.tasks.addTask(3, new EntityAIFleeSun(this, this.moveSpeed));
    049        this.tasks.addTask(5, new EntityAIWander(this, this.moveSpeed));
    050        this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
    051        this.tasks.addTask(6, 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
    055        if (par1World != null && !par1World.isRemote)
    056        {
    057            this.func_85036_m();
    058        }
    059    }
    060
    061    protected void entityInit()
    062    {
    063        super.entityInit();
    064        this.dataWatcher.addObject(13, new Byte((byte)0));
    065    }
    066
    067    /**
    068     * Returns true if the newer Entity AI code should be run
    069     */
    070    public boolean isAIEnabled()
    071    {
    072        return true;
    073    }
    074
    075    public int getMaxHealth()
    076    {
    077        return 20;
    078    }
    079
    080    /**
    081     * Returns the sound this mob makes while it's alive.
    082     */
    083    protected String getLivingSound()
    084    {
    085        return "mob.skeleton.say";
    086    }
    087
    088    /**
    089     * Returns the sound this mob makes when it is hurt.
    090     */
    091    protected String getHurtSound()
    092    {
    093        return "mob.skeleton.hurt";
    094    }
    095
    096    /**
    097     * Returns the sound this mob makes on death.
    098     */
    099    protected String getDeathSound()
    100    {
    101        return "mob.skeleton.death";
    102    }
    103
    104    /**
    105     * Plays step sound at given x, y, z for the entity
    106     */
    107    protected void playStepSound(int par1, int par2, int par3, int par4)
    108    {
    109        this.playSound("mob.skeleton.step", 0.15F, 1.0F);
    110    }
    111
    112    public boolean attackEntityAsMob(Entity par1Entity)
    113    {
    114        if (super.attackEntityAsMob(par1Entity))
    115        {
    116            if (this.getSkeletonType() == 1 && par1Entity instanceof EntityLiving)
    117            {
    118                ((EntityLiving)par1Entity).addPotionEffect(new PotionEffect(Potion.wither.id, 200));
    119            }
    120
    121            return true;
    122        }
    123        else
    124        {
    125            return false;
    126        }
    127    }
    128
    129    /**
    130     * Returns the amount of damage a mob should deal.
    131     */
    132    public int getAttackStrength(Entity par1Entity)
    133    {
    134        if (this.getSkeletonType() == 1)
    135        {
    136            ItemStack var2 = this.getHeldItem();
    137            int var3 = 4;
    138
    139            if (var2 != null)
    140            {
    141                var3 += var2.getDamageVsEntity(this);
    142            }
    143
    144            return var3;
    145        }
    146        else
    147        {
    148            return super.getAttackStrength(par1Entity);
    149        }
    150    }
    151
    152    /**
    153     * Get this Entity's EnumCreatureAttribute
    154     */
    155    public EnumCreatureAttribute getCreatureAttribute()
    156    {
    157        return EnumCreatureAttribute.UNDEAD;
    158    }
    159
    160    /**
    161     * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
    162     * use this to react to sunlight and start to burn.
    163     */
    164    public void onLivingUpdate()
    165    {
    166        if (this.worldObj.isDaytime() && !this.worldObj.isRemote)
    167        {
    168            float var1 = this.getBrightness(1.0F);
    169
    170            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)))
    171            {
    172                boolean var2 = true;
    173                ItemStack var3 = this.getCurrentItemOrArmor(4);
    174
    175                if (var3 != null)
    176                {
    177                    if (var3.isItemStackDamageable())
    178                    {
    179                        var3.setItemDamage(var3.getItemDamageForDisplay() + this.rand.nextInt(2));
    180
    181                        if (var3.getItemDamageForDisplay() >= var3.getMaxDamage())
    182                        {
    183                            this.renderBrokenItemStack(var3);
    184                            this.setCurrentItemOrArmor(4, (ItemStack)null);
    185                        }
    186                    }
    187
    188                    var2 = false;
    189                }
    190
    191                if (var2)
    192                {
    193                    this.setFire(8);
    194                }
    195            }
    196        }
    197
    198        super.onLivingUpdate();
    199    }
    200
    201    /**
    202     * Called when the mob's health reaches 0.
    203     */
    204    public void onDeath(DamageSource par1DamageSource)
    205    {
    206        super.onDeath(par1DamageSource);
    207
    208        if (par1DamageSource.getSourceOfDamage() instanceof EntityArrow && par1DamageSource.getEntity() instanceof EntityPlayer)
    209        {
    210            EntityPlayer var2 = (EntityPlayer)par1DamageSource.getEntity();
    211            double var3 = var2.posX - this.posX;
    212            double var5 = var2.posZ - this.posZ;
    213
    214            if (var3 * var3 + var5 * var5 >= 2500.0D)
    215            {
    216                var2.triggerAchievement(AchievementList.snipeSkeleton);
    217            }
    218        }
    219    }
    220
    221    /**
    222     * Returns the item ID for the item the mob drops on death.
    223     */
    224    protected int getDropItemId()
    225    {
    226        return Item.arrow.itemID;
    227    }
    228
    229    /**
    230     * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param
    231     * par2 - Level of Looting used to kill this mob.
    232     */
    233    protected void dropFewItems(boolean par1, int par2)
    234    {
    235        int var3;
    236        int var4;
    237
    238        if (this.getSkeletonType() == 1)
    239        {
    240            var3 = this.rand.nextInt(3 + par2) - 1;
    241
    242            for (var4 = 0; var4 < var3; ++var4)
    243            {
    244                this.dropItem(Item.coal.itemID, 1);
    245            }
    246        }
    247        else
    248        {
    249            var3 = this.rand.nextInt(3 + par2);
    250
    251            for (var4 = 0; var4 < var3; ++var4)
    252            {
    253                this.dropItem(Item.arrow.itemID, 1);
    254            }
    255        }
    256
    257        var3 = this.rand.nextInt(3 + par2);
    258
    259        for (var4 = 0; var4 < var3; ++var4)
    260        {
    261            this.dropItem(Item.bone.itemID, 1);
    262        }
    263    }
    264
    265    protected void dropRareDrop(int par1)
    266    {
    267        if (this.getSkeletonType() == 1)
    268        {
    269            this.entityDropItem(new ItemStack(Item.skull.itemID, 1, 1), 0.0F);
    270        }
    271    }
    272
    273    protected void func_82164_bB()
    274    {
    275        super.func_82164_bB();
    276        this.setCurrentItemOrArmor(0, new ItemStack(Item.bow));
    277    }
    278
    279    @SideOnly(Side.CLIENT)
    280
    281    /**
    282     * Returns the texture's file path as a String.
    283     */
    284    public String getTexture()
    285    {
    286        return this.getSkeletonType() == 1 ? "/mob/skeleton_wither.png" : super.getTexture();
    287    }
    288
    289    /**
    290     * Initialize this creature.
    291     */
    292    public void initCreature()
    293    {
    294        if (this.worldObj.provider instanceof WorldProviderHell && this.getRNG().nextInt(5) > 0)
    295        {
    296            this.tasks.addTask(4, this.aiAttackOnCollide);
    297            this.setSkeletonType(1);
    298            this.setCurrentItemOrArmor(0, new ItemStack(Item.swordStone));
    299        }
    300        else
    301        {
    302            this.tasks.addTask(4, this.aiArrowAttack);
    303            this.func_82164_bB();
    304            this.func_82162_bC();
    305        }
    306
    307        this.canPickUpLoot = this.rand.nextFloat() < pickUpLootProability[this.worldObj.difficultySetting];
    308
    309        if (this.getCurrentItemOrArmor(4) == null)
    310        {
    311            Calendar var1 = this.worldObj.getCurrentDate();
    312
    313            if (var1.get(2) + 1 == 10 && var1.get(5) == 31 && this.rand.nextFloat() < 0.25F)
    314            {
    315                this.setCurrentItemOrArmor(4, new ItemStack(this.rand.nextFloat() < 0.1F ? Block.pumpkinLantern : Block.pumpkin));
    316                this.equipmentDropChances[4] = 0.0F;
    317            }
    318        }
    319    }
    320
    321    public void func_85036_m()
    322    {
    323        this.tasks.func_85156_a(this.aiAttackOnCollide);
    324        this.tasks.func_85156_a(this.aiArrowAttack);
    325        ItemStack var1 = this.getHeldItem();
    326
    327        if (var1 != null && var1.itemID == Item.bow.itemID)
    328        {
    329            this.tasks.addTask(4, this.aiArrowAttack);
    330        }
    331        else
    332        {
    333            this.tasks.addTask(4, this.aiAttackOnCollide);
    334        }
    335    }
    336
    337    /**
    338     * Attack the specified entity using a ranged attack.
    339     */
    340    public void attackEntityWithRangedAttack(EntityLiving par1EntityLiving)
    341    {
    342        EntityArrow var2 = new EntityArrow(this.worldObj, this, par1EntityLiving, 1.6F, 12.0F);
    343        int var3 = EnchantmentHelper.getEnchantmentLevel(Enchantment.power.effectId, this.getHeldItem());
    344        int var4 = EnchantmentHelper.getEnchantmentLevel(Enchantment.punch.effectId, this.getHeldItem());
    345
    346        if (var3 > 0)
    347        {
    348            var2.setDamage(var2.getDamage() + (double)var3 * 0.5D + 0.5D);
    349        }
    350
    351        if (var4 > 0)
    352        {
    353            var2.setKnockbackStrength(var4);
    354        }
    355
    356        if (EnchantmentHelper.getEnchantmentLevel(Enchantment.flame.effectId, this.getHeldItem()) > 0 || this.getSkeletonType() == 1)
    357        {
    358            var2.setFire(100);
    359        }
    360
    361        this.playSound("random.bow", 1.0F, 1.0F / (this.getRNG().nextFloat() * 0.4F + 0.8F));
    362        this.worldObj.spawnEntityInWorld(var2);
    363    }
    364
    365    /**
    366     * Return this skeleton's type.
    367     */
    368    public int getSkeletonType()
    369    {
    370        return this.dataWatcher.getWatchableObjectByte(13);
    371    }
    372
    373    /**
    374     * Set this skeleton's type.
    375     */
    376    public void setSkeletonType(int par1)
    377    {
    378        this.dataWatcher.updateObject(13, Byte.valueOf((byte)par1));
    379        this.isImmuneToFire = par1 == 1;
    380
    381        if (par1 == 1)
    382        {
    383            this.setSize(0.72F, 2.16F);
    384        }
    385        else
    386        {
    387            this.setSize(0.6F, 1.8F);
    388        }
    389    }
    390
    391    /**
    392     * (abstract) Protected helper method to read subclass entity data from NBT.
    393     */
    394    public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    395    {
    396        super.readEntityFromNBT(par1NBTTagCompound);
    397
    398        if (par1NBTTagCompound.hasKey("SkeletonType"))
    399        {
    400            byte var2 = par1NBTTagCompound.getByte("SkeletonType");
    401            this.setSkeletonType(var2);
    402        }
    403
    404        this.func_85036_m();
    405    }
    406
    407    /**
    408     * (abstract) Protected helper method to write subclass entity data to NBT.
    409     */
    410    public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    411    {
    412        super.writeEntityToNBT(par1NBTTagCompound);
    413        par1NBTTagCompound.setByte("SkeletonType", (byte)this.getSkeletonType());
    414    }
    415
    416    /**
    417     * Sets the held item, or an armor slot. Slot 0 is held item. Slot 1-4 is armor. Params: Item, slot
    418     */
    419    public void setCurrentItemOrArmor(int par1, ItemStack par2ItemStack)
    420    {
    421        super.setCurrentItemOrArmor(par1, par2ItemStack);
    422
    423        if (!this.worldObj.isRemote && par1 == 0)
    424        {
    425            this.func_85036_m();
    426        }
    427    }
    428}