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    }