    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.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            this.canPickUpLoot = this.rand.nextFloat() < pickUpLootProability[this.worldObj.difficultySetting];
    307
    308            if (this.getCurrentItemOrArmor(4) == null)
    309            {
    310                Calendar var1 = this.worldObj.getCurrentDate();
    311
    312                if (var1.get(2) + 1 == 10 && var1.get(5) == 31 && this.rand.nextFloat() < 0.25F)
    313                {
    314                    this.setCurrentItemOrArmor(4, new ItemStack(this.rand.nextFloat() < 0.1F ? Block.pumpkinLantern : Block.pumpkin));
    315                    this.equipmentDropChances[4] = 0.0F;
    316                }
    317            }
    318        }
    319
    320        public void func_85036_m()
    321        {
    322            this.tasks.func_85156_a(this.field_85038_e);
    323            this.tasks.func_85156_a(this.field_85037_d);
    324            ItemStack var1 = this.getHeldItem();
    325
    326            if (var1 != null && var1.itemID == Item.bow.shiftedIndex)
    327            {
    328                this.tasks.addTask(4, this.field_85037_d);
    329            }
    330            else
    331            {
    332                this.tasks.addTask(4, this.field_85038_e);
    333            }
    334        }
    335
    336        /**
    337         * Attack the specified entity using a ranged attack.
    338         */
    339        public void attackEntityWithRangedAttack(EntityLiving par1EntityLiving)
    340        {
    341            EntityArrow var2 = new EntityArrow(this.worldObj, this, par1EntityLiving, 1.6F, 12.0F);
    342            int var3 = EnchantmentHelper.getEnchantmentLevel(Enchantment.power.effectId, this.getHeldItem());
    343            int var4 = EnchantmentHelper.getEnchantmentLevel(Enchantment.punch.effectId, this.getHeldItem());
    344
    345            if (var3 > 0)
    346            {
    347                var2.setDamage(var2.getDamage() + (double)var3 * 0.5D + 0.5D);
    348            }
    349
    350            if (var4 > 0)
    351            {
    352                var2.setKnockbackStrength(var4);
    353            }
    354
    355            if (EnchantmentHelper.getEnchantmentLevel(Enchantment.flame.effectId, this.getHeldItem()) > 0 || this.getSkeletonType() == 1)
    356            {
    357                var2.setFire(100);
    358            }
    359
    360            this.func_85030_a("random.bow", 1.0F, 1.0F / (this.getRNG().nextFloat() * 0.4F + 0.8F));
    361            this.worldObj.spawnEntityInWorld(var2);
    362        }
    363
    364        /**
    365         * Return this skeleton's type.
    366         */
    367        public int getSkeletonType()
    368        {
    369            return this.dataWatcher.getWatchableObjectByte(13);
    370        }
    371
    372        /**
    373         * Set this skeleton's type.
    374         */
    375        public void setSkeletonType(int par1)
    376        {
    377            this.dataWatcher.updateObject(13, Byte.valueOf((byte)par1));
    378            this.isImmuneToFire = par1 == 1;
    379
    380            if (par1 == 1)
    381            {
    382                this.setSize(0.72F, 2.16F);
    383            }
    384            else
    385            {
    386                this.setSize(0.6F, 1.8F);
    387            }
    388        }
    389
    390        /**
    391         * (abstract) Protected helper method to read subclass entity data from NBT.
    392         */
    393        public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    394        {
    395            super.readEntityFromNBT(par1NBTTagCompound);
    396
    397            if (par1NBTTagCompound.hasKey("SkeletonType"))
    398            {
    399                byte var2 = par1NBTTagCompound.getByte("SkeletonType");
    400                this.setSkeletonType(var2);
    401            }
    402
    403            this.func_85036_m();
    404        }
    405
    406        /**
    407         * (abstract) Protected helper method to write subclass entity data to NBT.
    408         */
    409        public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    410        {
    411            super.writeEntityToNBT(par1NBTTagCompound);
    412            par1NBTTagCompound.setByte("SkeletonType", (byte)this.getSkeletonType());
    413        }
    414
    415        /**
    416         * Sets the held item, or an armor slot. Slot 0 is held item. Slot 1-4 is armor. Params: Item, slot
    417         */
    418        public void setCurrentItemOrArmor(int par1, ItemStack par2ItemStack)
    419        {
    420            super.setCurrentItemOrArmor(par1, par2ItemStack);
    421
    422            if (!this.worldObj.isRemote && par1 == 0)
    423            {
    424                this.func_85036_m();
    425            }
    426        }
    427    }