    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    }