    001    package net.minecraft.entity.monster;
    002
    003    import cpw.mods.fml.common.Side;
    004    import cpw.mods.fml.common.asm.SideOnly;
    005    import java.util.Calendar;
    006    import net.minecraft.block.Block;
    007    import net.minecraft.enchantment.Enchantment;
    008    import net.minecraft.enchantment.EnchantmentHelper;
    009    import net.minecraft.entity.Entity;
    010    import net.minecraft.entity.EntityLiving;
    011    import net.minecraft.entity.EnumCreatureAttribute;
    012    import net.minecraft.entity.IRangedAttackMob;
    013    import net.minecraft.entity.ai.EntityAIArrowAttack;
    014    import net.minecraft.entity.ai.EntityAIAttackOnCollide;
    015    import net.minecraft.entity.ai.EntityAIFleeSun;
    016    import net.minecraft.entity.ai.EntityAIHurtByTarget;
    017    import net.minecraft.entity.ai.EntityAILookIdle;
    018    import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
    019    import net.minecraft.entity.ai.EntityAIRestrictSun;
    020    import net.minecraft.entity.ai.EntityAISwimming;
    021    import net.minecraft.entity.ai.EntityAIWander;
    022    import net.minecraft.entity.ai.EntityAIWatchClosest;
    023    import net.minecraft.entity.player.EntityPlayer;
    024    import net.minecraft.entity.projectile.EntityArrow;
    025    import net.minecraft.item.Item;
    026    import net.minecraft.item.ItemStack;
    027    import net.minecraft.nbt.NBTTagCompound;
    028    import net.minecraft.potion.Potion;
    029    import net.minecraft.potion.PotionEffect;
    030    import net.minecraft.stats.AchievementList;
    031    import net.minecraft.util.DamageSource;
    032    import net.minecraft.util.MathHelper;
    033    import net.minecraft.world.World;
    034    import net.minecraft.world.WorldProviderHell;
    035
    036    public class EntitySkeleton extends EntityMob implements IRangedAttackMob
    037    {
    038        private EntityAIArrowAttack field_85037_d = new EntityAIArrowAttack(this, 0.25F, 60, 10.0F);
    039        private EntityAIAttackOnCollide field_85038_e = 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.func_85030_a("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.shiftedIndex;
    227        }
    228
    229        /**
    230         * Drop 0-2 items of this living's type
    231         */
    232        protected void dropFewItems(boolean par1, int par2)
    233        {
    234            int var3;
    235            int var4;
    236
    237            if (this.getSkeletonType() == 1)
    238            {
    239                var3 = this.rand.nextInt(3 + par2) - 1;
    240
    241                for (var4 = 0; var4 < var3; ++var4)
    242                {
    243                    this.dropItem(Item.coal.shiftedIndex, 1);
    244                }
    245            }
    246            else
    247            {
    248                var3 = this.rand.nextInt(3 + par2);
    249
    250                for (var4 = 0; var4 < var3; ++var4)
    251                {
    252                    this.dropItem(Item.arrow.shiftedIndex, 1);
    253                }
    254            }
    255
    256            var3 = this.rand.nextInt(3 + par2);
    257
    258            for (var4 = 0; var4 < var3; ++var4)
    259            {
    260                this.dropItem(Item.bone.shiftedIndex, 1);
    261            }
    262        }
    263
    264        protected void dropRareDrop(int par1)
    265        {
    266            if (this.getSkeletonType() == 1)
    267            {
    268                this.entityDropItem(new ItemStack(Item.skull.shiftedIndex, 1, 1), 0.0F);
    269            }
    270        }
    271
    272        protected void func_82164_bB()
    273        {
    274            super.func_82164_bB();
    275            this.setCurrentItemOrArmor(0, new ItemStack(Item.bow));
    276        }
    277
    278        @SideOnly(Side.CLIENT)
    279
    280        /**
    281         * Returns the texture's file path as a String.
    282         */
    283        public String getTexture()
    284        {
    285            return this.getSkeletonType() == 1 ? "/mob/skeleton_wither.png" : super.getTexture();
    286        }
    287
    288        /**
    289         * Initialize this creature.
    290         */
    291        public void initCreature()
    292        {
    293            if (this.worldObj.provider instanceof WorldProviderHell && this.getRNG().nextInt(5) > 0)
    294            {
    295                this.tasks.addTask(4, this.field_85038_e);
    296                this.setSkeletonType(1);
    297                this.setCurrentItemOrArmor(0, new ItemStack(Item.swordStone));
    298            }
    299            else
    300            {
    301                this.tasks.addTask(4, this.field_85037_d);
    302                this.func_82164_bB();
    303                this.func_82162_bC();
    304            }
    305
    306            if (this.rand.nextFloat() >= field_82181_as[this.worldObj.difficultySetting])
    307            {
    308                ;
    309            }
    310
    311            this.canPickUpLoot = true;
    312
    313            if (this.getCurrentItemOrArmor(4) == null)
    314            {
    315                Calendar var1 = this.worldObj.getCurrentDate();
    316
    317                if (var1.get(2) + 1 == 10 && var1.get(5) == 31 && this.rand.nextFloat() < 0.25F)
    318                {
    319                    this.setCurrentItemOrArmor(4, new ItemStack(this.rand.nextFloat() < 0.1F ? Block.pumpkinLantern : Block.pumpkin));
    320                    this.equipmentDropChances[4] = 0.0F;
    321                }
    322            }
    323        }
    324
    325        public void func_85036_m()
    326        {
    327            this.tasks.func_85156_a(this.field_85038_e);
    328            this.tasks.func_85156_a(this.field_85037_d);
    329            ItemStack var1 = this.getHeldItem();
    330
    331            if (var1 != null && var1.itemID == Item.bow.shiftedIndex)
    332            {
    333                this.tasks.addTask(4, this.field_85037_d);
    334            }
    335            else
    336            {
    337                this.tasks.addTask(4, this.field_85038_e);
    338            }
    339        }
    340
    341        /**
    342         * Attack the specified entity using a ranged attack.
    343         */
    344        public void attackEntityWithRangedAttack(EntityLiving par1EntityLiving)
    345        {
    346            EntityArrow var2 = new EntityArrow(this.worldObj, this, par1EntityLiving, 1.6F, 12.0F);
    347            int var3 = EnchantmentHelper.getEnchantmentLevel(Enchantment.power.effectId, this.getHeldItem());
    348            int var4 = EnchantmentHelper.getEnchantmentLevel(Enchantment.punch.effectId, this.getHeldItem());
    349
    350            if (var3 > 0)
    351            {
    352                var2.setDamage(var2.getDamage() + (double)var3 * 0.5D + 0.5D);
    353            }
    354
    355            if (var4 > 0)
    356            {
    357                var2.setKnockbackStrength(var4);
    358            }
    359
    360            if (EnchantmentHelper.getEnchantmentLevel(Enchantment.flame.effectId, this.getHeldItem()) > 0 || this.getSkeletonType() == 1)
    361            {
    362                var2.setFire(100);
    363            }
    364
    365            this.func_85030_a("random.bow", 1.0F, 1.0F / (this.getRNG().nextFloat() * 0.4F + 0.8F));
    366            this.worldObj.spawnEntityInWorld(var2);
    367        }
    368
    369        /**
    370         * Return this skeleton's type.
    371         */
    372        public int getSkeletonType()
    373        {
    374            return this.dataWatcher.getWatchableObjectByte(13);
    375        }
    376
    377        /**
    378         * Set this skeleton's type.
    379         */
    380        public void setSkeletonType(int par1)
    381        {
    382            this.dataWatcher.updateObject(13, Byte.valueOf((byte)par1));
    383            this.isImmuneToFire = par1 == 1;
    384
    385            if (par1 == 1)
    386            {
    387                this.setSize(0.72F, 2.16F);
    388            }
    389            else
    390            {
    391                this.setSize(0.6F, 1.8F);
    392            }
    393        }
    394
    395        /**
    396         * (abstract) Protected helper method to read subclass entity data from NBT.
    397         */
    398        public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    399        {
    400            super.readEntityFromNBT(par1NBTTagCompound);
    401
    402            if (par1NBTTagCompound.hasKey("SkeletonType"))
    403            {
    404                byte var2 = par1NBTTagCompound.getByte("SkeletonType");
    405                this.setSkeletonType(var2);
    406            }
    407
    408            this.func_85036_m();
    409        }
    410
    411        /**
    412         * (abstract) Protected helper method to write subclass entity data to NBT.
    413         */
    414        public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    415        {
    416            super.writeEntityToNBT(par1NBTTagCompound);
    417            par1NBTTagCompound.setByte("SkeletonType", (byte)this.getSkeletonType());
    418        }
    419
    420        /**
    421         * Sets the held item, or an armor slot. Slot 0 is held item. Slot 1-4 is armor. Params: Item, slot
    422         */
    423        public void setCurrentItemOrArmor(int par1, ItemStack par2ItemStack)
    424        {
    425            super.setCurrentItemOrArmor(par1, par2ItemStack);
    426
    427            if (!this.worldObj.isRemote && par1 == 0)
    428            {
    429                this.func_85036_m();
    430            }
    431        }
    432    }