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 }