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