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