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 }