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.Iterator;
006 import java.util.List;
007
008 public class EntityWitch extends EntityMob implements IRangedAttackMob
009 {
010 /** List of items a witch should drop on death. */
011 private static final int[] witchDrops = new int[] {Item.lightStoneDust.shiftedIndex, Item.sugar.shiftedIndex, Item.redstone.shiftedIndex, Item.spiderEye.shiftedIndex, Item.glassBottle.shiftedIndex, Item.gunpowder.shiftedIndex, Item.stick.shiftedIndex, Item.stick.shiftedIndex};
012
013 /**
014 * Timer used as interval for a witch's attack, decremented every tick if aggressive and when reaches zero the witch
015 * will throw a potion at the target entity.
016 */
017 private int witchAttackTimer = 0;
018
019 public EntityWitch(World par1World)
020 {
021 super(par1World);
022 this.texture = "/mob/villager/witch.png";
023 this.moveSpeed = 0.25F;
024 this.tasks.addTask(1, new EntityAISwimming(this));
025 this.tasks.addTask(2, new EntityAIArrowAttack(this, this.moveSpeed, 60, 10.0F));
026 this.tasks.addTask(2, new EntityAIWander(this, this.moveSpeed));
027 this.tasks.addTask(3, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
028 this.tasks.addTask(3, 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 }
032
033 protected void entityInit()
034 {
035 super.entityInit();
036 this.getDataWatcher().addObject(21, Byte.valueOf((byte)0));
037 }
038
039 /**
040 * Returns the sound this mob makes while it's alive.
041 */
042 protected String getLivingSound()
043 {
044 return "mob.witch.idle";
045 }
046
047 /**
048 * Returns the sound this mob makes when it is hurt.
049 */
050 protected String getHurtSound()
051 {
052 return "mob.witch.hurt";
053 }
054
055 /**
056 * Returns the sound this mob makes on death.
057 */
058 protected String getDeathSound()
059 {
060 return "mob.witch.death";
061 }
062
063 /**
064 * Set whether this witch is aggressive at an entity.
065 */
066 public void setAggressive(boolean par1)
067 {
068 this.getDataWatcher().updateObject(21, Byte.valueOf((byte)(par1 ? 1 : 0)));
069 }
070
071 /**
072 * Return whether this witch is aggressive at an entity.
073 */
074 public boolean getAggressive()
075 {
076 return this.getDataWatcher().getWatchableObjectByte(21) == 1;
077 }
078
079 public int getMaxHealth()
080 {
081 return 26;
082 }
083
084 /**
085 * Returns true if the newer Entity AI code should be run
086 */
087 public boolean isAIEnabled()
088 {
089 return true;
090 }
091
092 /**
093 * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
094 * use this to react to sunlight and start to burn.
095 */
096 public void onLivingUpdate()
097 {
098 if (!this.worldObj.isRemote)
099 {
100 if (this.getAggressive())
101 {
102 if (this.witchAttackTimer-- <= 0)
103 {
104 this.setAggressive(false);
105 ItemStack var1 = this.getHeldItem();
106 this.setCurrentItemOrArmor(0, (ItemStack)null);
107
108 if (var1 != null && var1.itemID == Item.potion.shiftedIndex)
109 {
110 List var2 = Item.potion.getEffects(var1);
111
112 if (var2 != null)
113 {
114 Iterator var3 = var2.iterator();
115
116 while (var3.hasNext())
117 {
118 PotionEffect var4 = (PotionEffect)var3.next();
119 this.addPotionEffect(new PotionEffect(var4));
120 }
121 }
122 }
123 }
124 }
125 else
126 {
127 short var5 = -1;
128
129 if (this.rand.nextFloat() < 0.15F && this.isBurning() && !this.isPotionActive(Potion.fireResistance))
130 {
131 var5 = 16307;
132 }
133 else if (this.rand.nextFloat() < 0.05F && this.health < this.getMaxHealth())
134 {
135 var5 = 16341;
136 }
137 else if (this.rand.nextFloat() < 0.25F && this.getAttackTarget() != null && !this.isPotionActive(Potion.moveSpeed) && this.getAttackTarget().getDistanceSqToEntity(this) > 121.0D)
138 {
139 var5 = 16274;
140 }
141 else if (this.rand.nextFloat() < 0.25F && this.getAttackTarget() != null && !this.isPotionActive(Potion.moveSpeed) && this.getAttackTarget().getDistanceSqToEntity(this) > 121.0D)
142 {
143 var5 = 16274;
144 }
145
146 if (var5 > -1)
147 {
148 this.setCurrentItemOrArmor(0, new ItemStack(Item.potion, 1, var5));
149 this.witchAttackTimer = this.getHeldItem().getMaxItemUseDuration();
150 this.setAggressive(true);
151 }
152 }
153
154 if (this.rand.nextFloat() < 7.5E-4F)
155 {
156 this.worldObj.setEntityState(this, (byte)15);
157 }
158 }
159
160 super.onLivingUpdate();
161 }
162
163 /**
164 * Reduces damage, depending on potions
165 */
166 protected int applyPotionDamageCalculations(DamageSource par1DamageSource, int par2)
167 {
168 par2 = super.applyPotionDamageCalculations(par1DamageSource, par2);
169
170 if (par1DamageSource.getEntity() == this)
171 {
172 par2 = 0;
173 }
174
175 if (par1DamageSource.isMagicDamage())
176 {
177 par2 = (int)((double)par2 * 0.15D);
178 }
179
180 return par2;
181 }
182
183 @SideOnly(Side.CLIENT)
184 public void handleHealthUpdate(byte par1)
185 {
186 if (par1 == 15)
187 {
188 for (int var2 = 0; var2 < this.rand.nextInt(35) + 10; ++var2)
189 {
190 this.worldObj.spawnParticle("witchMagic", this.posX + this.rand.nextGaussian() * 0.12999999523162842D, this.boundingBox.maxY + 0.5D + this.rand.nextGaussian() * 0.12999999523162842D, this.posZ + this.rand.nextGaussian() * 0.12999999523162842D, 0.0D, 0.0D, 0.0D);
191 }
192 }
193 else
194 {
195 super.handleHealthUpdate(par1);
196 }
197 }
198
199 /**
200 * This method returns a value to be applied directly to entity speed, this factor is less than 1 when a slowdown
201 * potion effect is applied, more than 1 when a haste potion effect is applied and 2 for fleeing entities.
202 */
203 public float getSpeedModifier()
204 {
205 float var1 = super.getSpeedModifier();
206
207 if (this.getAggressive())
208 {
209 var1 *= 0.75F;
210 }
211
212 return var1;
213 }
214
215 /**
216 * Drop 0-2 items of this living's type
217 */
218 protected void dropFewItems(boolean par1, int par2)
219 {
220 int var3 = this.rand.nextInt(3) + 1;
221
222 for (int var4 = 0; var4 < var3; ++var4)
223 {
224 int var5 = this.rand.nextInt(3);
225 int var6 = witchDrops[this.rand.nextInt(witchDrops.length)];
226
227 if (par2 > 0)
228 {
229 var5 += this.rand.nextInt(par2 + 1);
230 }
231
232 for (int var7 = 0; var7 < var5; ++var7)
233 {
234 this.dropItem(var6, 1);
235 }
236 }
237 }
238
239 /**
240 * Attack the specified entity using a ranged attack.
241 */
242 public void attackEntityWithRangedAttack(EntityLiving par1EntityLiving)
243 {
244 if (!this.getAggressive())
245 {
246 EntityPotion var2 = new EntityPotion(this.worldObj, this, 32732);
247 var2.rotationPitch -= -20.0F;
248 double var3 = par1EntityLiving.posX + par1EntityLiving.motionX - this.posX;
249 double var5 = par1EntityLiving.posY + (double)par1EntityLiving.getEyeHeight() - 1.100000023841858D - this.posY;
250 double var7 = par1EntityLiving.posZ + par1EntityLiving.motionZ - this.posZ;
251 float var9 = MathHelper.sqrt_double(var3 * var3 + var7 * var7);
252
253 if (var9 >= 8.0F && !par1EntityLiving.isPotionActive(Potion.moveSlowdown))
254 {
255 var2.setPotionDamage(32698);
256 }
257 else if (par1EntityLiving.getHealth() >= 8 && !par1EntityLiving.isPotionActive(Potion.poison))
258 {
259 var2.setPotionDamage(32660);
260 }
261 else if (var9 <= 3.0F && !par1EntityLiving.isPotionActive(Potion.weakness) && this.rand.nextFloat() < 0.25F)
262 {
263 var2.setPotionDamage(32696);
264 }
265
266 var2.setThrowableHeading(var3, var5 + (double)(var9 * 0.2F), var7, 0.75F, 8.0F);
267 this.worldObj.spawnEntityInWorld(var2);
268 }
269 }
270 }