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 }