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