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