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