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.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.field_85038_e);
297                this.setSkeletonType(1);
298                this.setCurrentItemOrArmor(0, new ItemStack(Item.swordStone));
299            }
300            else
301            {
302                this.tasks.addTask(4, this.field_85037_d);
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.field_85038_e);
324            this.tasks.func_85156_a(this.field_85037_d);
325            ItemStack var1 = this.getHeldItem();
326    
327            if (var1 != null && var1.itemID == Item.bow.itemID)
328            {
329                this.tasks.addTask(4, this.field_85037_d);
330            }
331            else
332            {
333                this.tasks.addTask(4, this.field_85038_e);
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    }