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