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 EntityZombie extends EntityMob 008 { 009 /** 010 * Ticker used to determine the time remaining for this zombie to convert into a villager when cured. 011 */ 012 private int conversionTime = 0; 013 014 public EntityZombie(World par1World) 015 { 016 super(par1World); 017 this.texture = "/mob/zombie.png"; 018 this.moveSpeed = 0.23F; 019 this.getNavigator().setBreakDoors(true); 020 this.tasks.addTask(0, new EntityAISwimming(this)); 021 this.tasks.addTask(1, new EntityAIBreakDoor(this)); 022 this.tasks.addTask(2, new EntityAIAttackOnCollide(this, EntityPlayer.class, this.moveSpeed, false)); 023 this.tasks.addTask(3, new EntityAIAttackOnCollide(this, EntityVillager.class, this.moveSpeed, true)); 024 this.tasks.addTask(4, new EntityAIMoveTwardsRestriction(this, this.moveSpeed)); 025 this.tasks.addTask(5, new EntityAIMoveThroughVillage(this, this.moveSpeed, false)); 026 this.tasks.addTask(6, new EntityAIWander(this, this.moveSpeed)); 027 this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); 028 this.tasks.addTask(7, new EntityAILookIdle(this)); 029 this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); 030 this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, 16.0F, 0, true)); 031 this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityVillager.class, 16.0F, 0, false)); 032 } 033 034 /** 035 * This method returns a value to be applied directly to entity speed, this factor is less than 1 when a slowdown 036 * potion effect is applied, more than 1 when a haste potion effect is applied and 2 for fleeing entities. 037 */ 038 public float getSpeedModifier() 039 { 040 return super.getSpeedModifier() * (this.isChild() ? 1.5F : 1.0F); 041 } 042 043 protected void entityInit() 044 { 045 super.entityInit(); 046 this.getDataWatcher().addObject(12, Byte.valueOf((byte)0)); 047 this.getDataWatcher().addObject(13, Byte.valueOf((byte)0)); 048 this.getDataWatcher().addObject(14, Byte.valueOf((byte)0)); 049 } 050 051 @SideOnly(Side.CLIENT) 052 053 /** 054 * Returns the texture's file path as a String. 055 */ 056 public String getTexture() 057 { 058 return this.isVillager() ? "/mob/zombie_villager.png" : "/mob/zombie.png"; 059 } 060 061 public int getMaxHealth() 062 { 063 return 20; 064 } 065 066 /** 067 * Returns the current armor value as determined by a call to InventoryPlayer.getTotalArmorValue 068 */ 069 public int getTotalArmorValue() 070 { 071 int var1 = super.getTotalArmorValue() + 2; 072 073 if (var1 > 20) 074 { 075 var1 = 20; 076 } 077 078 return var1; 079 } 080 081 /** 082 * Returns true if the newer Entity AI code should be run 083 */ 084 protected boolean isAIEnabled() 085 { 086 return true; 087 } 088 089 /** 090 * If Animal, checks if the age timer is negative 091 */ 092 public boolean isChild() 093 { 094 return this.getDataWatcher().getWatchableObjectByte(12) == 1; 095 } 096 097 /** 098 * Set whether this zombie is a child. 099 */ 100 public void setChild(boolean par1) 101 { 102 this.getDataWatcher().updateObject(12, Byte.valueOf((byte)1)); 103 } 104 105 /** 106 * Return whether this zombie is a villager. 107 */ 108 public boolean isVillager() 109 { 110 return this.getDataWatcher().getWatchableObjectByte(13) == 1; 111 } 112 113 /** 114 * Set whether this zombie is a villager. 115 */ 116 public void setVillager(boolean par1) 117 { 118 this.getDataWatcher().updateObject(13, Byte.valueOf((byte)(par1 ? 1 : 0))); 119 } 120 121 /** 122 * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons 123 * use this to react to sunlight and start to burn. 124 */ 125 public void onLivingUpdate() 126 { 127 if (this.worldObj.isDaytime() && !this.worldObj.isRemote && !this.isChild()) 128 { 129 float var1 = this.getBrightness(1.0F); 130 131 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))) 132 { 133 boolean var2 = true; 134 ItemStack var3 = this.getCurrentItemOrArmor(4); 135 136 if (var3 != null) 137 { 138 if (var3.isItemStackDamageable()) 139 { 140 var3.setItemDamage(var3.getItemDamageForDisplay() + this.rand.nextInt(2)); 141 142 if (var3.getItemDamageForDisplay() >= var3.getMaxDamage()) 143 { 144 this.renderBrokenItemStack(var3); 145 this.setCurrentItemOrArmor(4, (ItemStack)null); 146 } 147 } 148 149 var2 = false; 150 } 151 152 if (var2) 153 { 154 this.setFire(8); 155 } 156 } 157 } 158 159 super.onLivingUpdate(); 160 } 161 162 /** 163 * Called to update the entity's position/logic. 164 */ 165 public void onUpdate() 166 { 167 if (!this.worldObj.isRemote && this.func_82230_o()) 168 { 169 int var1 = this.getConversionTimeBoost(); 170 this.conversionTime -= var1; 171 172 if (this.conversionTime <= 0) 173 { 174 this.convertToVillager(); 175 } 176 } 177 178 super.onUpdate(); 179 } 180 181 /** 182 * Returns the amount of damage a mob should deal. 183 */ 184 public int getAttackStrength(Entity par1Entity) 185 { 186 ItemStack var2 = this.getHeldItem(); 187 int var3 = 4; 188 189 if (var2 != null) 190 { 191 var3 += var2.getDamageVsEntity(this); 192 } 193 194 return var3; 195 } 196 197 /** 198 * Returns the sound this mob makes while it's alive. 199 */ 200 protected String getLivingSound() 201 { 202 return "mob.zombie.say"; 203 } 204 205 /** 206 * Returns the sound this mob makes when it is hurt. 207 */ 208 protected String getHurtSound() 209 { 210 return "mob.zombie.hurt"; 211 } 212 213 /** 214 * Returns the sound this mob makes on death. 215 */ 216 protected String getDeathSound() 217 { 218 return "mob.zombie.death"; 219 } 220 221 /** 222 * Plays step sound at given x, y, z for the entity 223 */ 224 protected void playStepSound(int par1, int par2, int par3, int par4) 225 { 226 this.worldObj.playSoundAtEntity(this, "mob.zombie.step", 0.15F, 1.0F); 227 } 228 229 /** 230 * Returns the item ID for the item the mob drops on death. 231 */ 232 protected int getDropItemId() 233 { 234 return Item.rottenFlesh.shiftedIndex; 235 } 236 237 /** 238 * Get this Entity's EnumCreatureAttribute 239 */ 240 public EnumCreatureAttribute getCreatureAttribute() 241 { 242 return EnumCreatureAttribute.UNDEAD; 243 } 244 245 protected void dropRareDrop(int par1) 246 { 247 switch (this.rand.nextInt(3)) 248 { 249 case 0: 250 this.dropItem(Item.ingotIron.shiftedIndex, 1); 251 break; 252 case 1: 253 this.dropItem(Item.carrot.shiftedIndex, 1); 254 break; 255 case 2: 256 this.dropItem(Item.potatoe.shiftedIndex, 1); 257 } 258 } 259 260 protected void func_82164_bB() 261 { 262 super.func_82164_bB(); 263 264 if (this.rand.nextFloat() < (this.worldObj.difficultySetting == 3 ? 0.05F : 0.01F)) 265 { 266 int var1 = this.rand.nextInt(3); 267 268 if (var1 == 0) 269 { 270 this.setCurrentItemOrArmor(0, new ItemStack(Item.swordSteel)); 271 } 272 else 273 { 274 this.setCurrentItemOrArmor(0, new ItemStack(Item.shovelSteel)); 275 } 276 } 277 } 278 279 /** 280 * (abstract) Protected helper method to write subclass entity data to NBT. 281 */ 282 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 283 { 284 super.writeEntityToNBT(par1NBTTagCompound); 285 286 if (this.isChild()) 287 { 288 par1NBTTagCompound.setBoolean("IsBaby", true); 289 } 290 291 if (this.isVillager()) 292 { 293 par1NBTTagCompound.setBoolean("IsVillager", true); 294 } 295 296 par1NBTTagCompound.setInteger("ConversionTime", this.func_82230_o() ? this.conversionTime : -1); 297 } 298 299 /** 300 * (abstract) Protected helper method to read subclass entity data from NBT. 301 */ 302 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 303 { 304 super.readEntityFromNBT(par1NBTTagCompound); 305 306 if (par1NBTTagCompound.getBoolean("IsBaby")) 307 { 308 this.setChild(true); 309 } 310 311 if (par1NBTTagCompound.getBoolean("IsVillager")) 312 { 313 this.setVillager(true); 314 } 315 316 if (par1NBTTagCompound.hasKey("ConversionTime") && par1NBTTagCompound.getInteger("ConversionTime") > -1) 317 { 318 this.startConversion(par1NBTTagCompound.getInteger("ConversionTime")); 319 } 320 } 321 322 /** 323 * This method gets called when the entity kills another one. 324 */ 325 public void onKillEntity(EntityLiving par1EntityLiving) 326 { 327 super.onKillEntity(par1EntityLiving); 328 329 if (this.worldObj.difficultySetting >= 2 && par1EntityLiving instanceof EntityVillager) 330 { 331 if (this.worldObj.difficultySetting == 2 && this.rand.nextBoolean()) 332 { 333 return; 334 } 335 336 EntityZombie var2 = new EntityZombie(this.worldObj); 337 var2.func_82149_j(par1EntityLiving); 338 this.worldObj.setEntityDead(par1EntityLiving); 339 var2.initCreature(); 340 var2.setVillager(true); 341 342 if (par1EntityLiving.isChild()) 343 { 344 var2.setChild(true); 345 } 346 347 this.worldObj.spawnEntityInWorld(var2); 348 this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1016, (int)this.posX, (int)this.posY, (int)this.posZ, 0); 349 } 350 } 351 352 /** 353 * Initialize this creature. 354 */ 355 public void initCreature() 356 { 357 this.canPickUpLoot = this.rand.nextFloat() < field_82181_as[this.worldObj.difficultySetting]; 358 359 if (this.worldObj.rand.nextFloat() < 0.05F) 360 { 361 this.setVillager(true); 362 } 363 364 this.func_82164_bB(); 365 this.func_82162_bC(); 366 367 if (this.getCurrentItemOrArmor(4) == null) 368 { 369 Calendar var1 = this.worldObj.getCurrentDate(); 370 371 if (var1.get(2) + 1 == 10 && var1.get(5) == 31 && this.rand.nextFloat() < 0.25F) 372 { 373 this.setCurrentItemOrArmor(4, new ItemStack(this.rand.nextFloat() < 0.1F ? Block.pumpkinLantern : Block.pumpkin)); 374 this.equipmentDropChances[4] = 0.0F; 375 } 376 } 377 } 378 379 /** 380 * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig. 381 */ 382 public boolean interact(EntityPlayer par1EntityPlayer) 383 { 384 ItemStack var2 = par1EntityPlayer.getCurrentEquippedItem(); 385 386 if (var2 != null && var2.getItem() == Item.appleGold && var2.getItemDamage() == 0 && this.isVillager() && this.isPotionActive(Potion.weakness)) 387 { 388 if (!par1EntityPlayer.capabilities.isCreativeMode) 389 { 390 --var2.stackSize; 391 } 392 393 if (var2.stackSize <= 0) 394 { 395 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null); 396 } 397 398 if (!this.worldObj.isRemote) 399 { 400 this.startConversion(this.rand.nextInt(2401) + 3600); 401 } 402 403 return true; 404 } 405 else 406 { 407 return false; 408 } 409 } 410 411 /** 412 * Starts converting this zombie into a villager. The zombie converts into a villager after the specified time in 413 * ticks. 414 */ 415 protected void startConversion(int par1) 416 { 417 this.conversionTime = par1; 418 this.getDataWatcher().updateObject(14, Byte.valueOf((byte)1)); 419 this.removePotionEffect(Potion.weakness.id); 420 this.addPotionEffect(new PotionEffect(Potion.damageBoost.id, par1, Math.min(this.worldObj.difficultySetting - 1, 0))); 421 this.worldObj.setEntityState(this, (byte)16); 422 } 423 424 @SideOnly(Side.CLIENT) 425 public void handleHealthUpdate(byte par1) 426 { 427 if (par1 == 16) 428 { 429 this.worldObj.playSound(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "mob.zombie.remedy", 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); 430 } 431 else 432 { 433 super.handleHealthUpdate(par1); 434 } 435 } 436 437 public boolean func_82230_o() 438 { 439 return this.getDataWatcher().getWatchableObjectByte(14) == 1; 440 } 441 442 /** 443 * Convert this zombie into a villager. 444 */ 445 protected void convertToVillager() 446 { 447 EntityVillager var1 = new EntityVillager(this.worldObj); 448 var1.func_82149_j(this); 449 var1.initCreature(); 450 var1.func_82187_q(); 451 452 if (this.isChild()) 453 { 454 var1.setGrowingAge(-24000); 455 } 456 457 this.worldObj.setEntityDead(this); 458 this.worldObj.spawnEntityInWorld(var1); 459 var1.addPotionEffect(new PotionEffect(Potion.confusion.id, 200, 0)); 460 this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1017, (int)this.posX, (int)this.posY, (int)this.posZ, 0); 461 } 462 463 /** 464 * Return the amount of time decremented from conversionTime every tick. 465 */ 466 protected int getConversionTimeBoost() 467 { 468 int var1 = 1; 469 470 if (this.rand.nextFloat() < 0.01F) 471 { 472 int var2 = 0; 473 474 for (int var3 = (int)this.posX - 4; var3 < (int)this.posX + 4 && var2 < 14; ++var3) 475 { 476 for (int var4 = (int)this.posY - 4; var4 < (int)this.posY + 4 && var2 < 14; ++var4) 477 { 478 for (int var5 = (int)this.posZ - 4; var5 < (int)this.posZ + 4 && var2 < 14; ++var5) 479 { 480 int var6 = this.worldObj.getBlockId(var3, var4, var5); 481 482 if (var6 == Block.fenceIron.blockID || var6 == Block.bed.blockID) 483 { 484 if (this.rand.nextFloat() < 0.3F) 485 { 486 ++var1; 487 } 488 489 ++var2; 490 } 491 } 492 } 493 } 494 } 495 496 return var1; 497 } 498 }