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    }