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 }