    001    package net.minecraft.entity.monster;
    002
    003    import net.minecraft.entity.Entity;
    004    import net.minecraft.entity.EntityFlying;
    005    import net.minecraft.entity.player.EntityPlayer;
    006    import net.minecraft.entity.projectile.EntityLargeFireball;
    007    import net.minecraft.item.Item;
    008    import net.minecraft.nbt.NBTTagCompound;
    009    import net.minecraft.stats.AchievementList;
    010    import net.minecraft.util.AxisAlignedBB;
    011    import net.minecraft.util.DamageSource;
    012    import net.minecraft.util.MathHelper;
    013    import net.minecraft.util.Vec3;
    014    import net.minecraft.world.World;
    015
    016    public class EntityGhast extends EntityFlying implements IMob
    017    {
    018        public int courseChangeCooldown = 0;
    019        public double waypointX;
    020        public double waypointY;
    021        public double waypointZ;
    022        private Entity targetedEntity = null;
    023
    024        /** Cooldown time between target loss and new target aquirement. */
    025        private int aggroCooldown = 0;
    026        public int prevAttackCounter = 0;
    027        public int attackCounter = 0;
    028        private int field_92009_j = 1;
    029
    030        public EntityGhast(World par1World)
    031        {
    032            super(par1World);
    033            this.texture = "/mob/ghast.png";
    034            this.setSize(4.0F, 4.0F);
    035            this.isImmuneToFire = true;
    036            this.experienceValue = 5;
    037        }
    038
    039        /**
    040         * Called when the entity is attacked.
    041         */
    042        public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
    043        {
    044            if (this.func_85032_ar())
    045            {
    046                return false;
    047            }
    048            else if ("fireball".equals(par1DamageSource.getDamageType()) && par1DamageSource.getEntity() instanceof EntityPlayer)
    049            {
    050                super.attackEntityFrom(par1DamageSource, 1000);
    051                ((EntityPlayer)par1DamageSource.getEntity()).triggerAchievement(AchievementList.ghast);
    052                return true;
    053            }
    054            else
    055            {
    056                return super.attackEntityFrom(par1DamageSource, par2);
    057            }
    058        }
    059
    060        protected void entityInit()
    061        {
    062            super.entityInit();
    063            this.dataWatcher.addObject(16, Byte.valueOf((byte)0));
    064        }
    065
    066        public int getMaxHealth()
    067        {
    068            return 10;
    069        }
    070
    071        /**
    072         * Called to update the entity's position/logic.
    073         */
    074        public void onUpdate()
    075        {
    076            super.onUpdate();
    077            byte var1 = this.dataWatcher.getWatchableObjectByte(16);
    078            this.texture = var1 == 1 ? "/mob/ghast_fire.png" : "/mob/ghast.png";
    079        }
    080
    081        protected void updateEntityActionState()
    082        {
    083            if (!this.worldObj.isRemote && this.worldObj.difficultySetting == 0)
    084            {
    085                this.setDead();
    086            }
    087
    088            this.despawnEntity();
    089            this.prevAttackCounter = this.attackCounter;
    090            double var1 = this.waypointX - this.posX;
    091            double var3 = this.waypointY - this.posY;
    092            double var5 = this.waypointZ - this.posZ;
    093            double var7 = var1 * var1 + var3 * var3 + var5 * var5;
    094
    095            if (var7 < 1.0D || var7 > 3600.0D)
    096            {
    097                this.waypointX = this.posX + (double)((this.rand.nextFloat() * 2.0F - 1.0F) * 16.0F);
    098                this.waypointY = this.posY + (double)((this.rand.nextFloat() * 2.0F - 1.0F) * 16.0F);
    099                this.waypointZ = this.posZ + (double)((this.rand.nextFloat() * 2.0F - 1.0F) * 16.0F);
    100            }
    101
    102            if (this.courseChangeCooldown-- <= 0)
    103            {
    104                this.courseChangeCooldown += this.rand.nextInt(5) + 2;
    105                var7 = (double)MathHelper.sqrt_double(var7);
    106
    107                if (this.isCourseTraversable(this.waypointX, this.waypointY, this.waypointZ, var7))
    108                {
    109                    this.motionX += var1 / var7 * 0.1D;
    110                    this.motionY += var3 / var7 * 0.1D;
    111                    this.motionZ += var5 / var7 * 0.1D;
    112                }
    113                else
    114                {
    115                    this.waypointX = this.posX;
    116                    this.waypointY = this.posY;
    117                    this.waypointZ = this.posZ;
    118                }
    119            }
    120
    121            if (this.targetedEntity != null && this.targetedEntity.isDead)
    122            {
    123                this.targetedEntity = null;
    124            }
    125
    126            if (this.targetedEntity == null || this.aggroCooldown-- <= 0)
    127            {
    128                this.targetedEntity = this.worldObj.getClosestVulnerablePlayerToEntity(this, 100.0D);
    129
    130                if (this.targetedEntity != null)
    131                {
    132                    this.aggroCooldown = 20;
    133                }
    134            }
    135
    136            double var9 = 64.0D;
    137
    138            if (this.targetedEntity != null && this.targetedEntity.getDistanceSqToEntity(this) < var9 * var9)
    139            {
    140                double var11 = this.targetedEntity.posX - this.posX;
    141                double var13 = this.targetedEntity.boundingBox.minY + (double)(this.targetedEntity.height / 2.0F) - (this.posY + (double)(this.height / 2.0F));
    142                double var15 = this.targetedEntity.posZ - this.posZ;
    143                this.renderYawOffset = this.rotationYaw = -((float)Math.atan2(var11, var15)) * 180.0F / (float)Math.PI;
    144
    145                if (this.canEntityBeSeen(this.targetedEntity))
    146                {
    147                    if (this.attackCounter == 10)
    148                    {
    149                        this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1007, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
    150                    }
    151
    152                    ++this.attackCounter;
    153
    154                    if (this.attackCounter == 20)
    155                    {
    156                        this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1008, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
    157                        EntityLargeFireball var17 = new EntityLargeFireball(this.worldObj, this, var11, var13, var15);
    158                        var17.field_92012_e = this.field_92009_j;
    159                        double var18 = 4.0D;
    160                        Vec3 var20 = this.getLook(1.0F);
    161                        var17.posX = this.posX + var20.xCoord * var18;
    162                        var17.posY = this.posY + (double)(this.height / 2.0F) + 0.5D;
    163                        var17.posZ = this.posZ + var20.zCoord * var18;
    164                        this.worldObj.spawnEntityInWorld(var17);
    165                        this.attackCounter = -40;
    166                    }
    167                }
    168                else if (this.attackCounter > 0)
    169                {
    170                    --this.attackCounter;
    171                }
    172            }
    173            else
    174            {
    175                this.renderYawOffset = this.rotationYaw = -((float)Math.atan2(this.motionX, this.motionZ)) * 180.0F / (float)Math.PI;
    176
    177                if (this.attackCounter > 0)
    178                {
    179                    --this.attackCounter;
    180                }
    181            }
    182
    183            if (!this.worldObj.isRemote)
    184            {
    185                byte var21 = this.dataWatcher.getWatchableObjectByte(16);
    186                byte var12 = (byte)(this.attackCounter > 10 ? 1 : 0);
    187
    188                if (var21 != var12)
    189                {
    190                    this.dataWatcher.updateObject(16, Byte.valueOf(var12));
    191                }
    192            }
    193        }
    194
    195        /**
    196         * True if the ghast has an unobstructed line of travel to the waypoint.
    197         */
    198        private boolean isCourseTraversable(double par1, double par3, double par5, double par7)
    199        {
    200            double var9 = (this.waypointX - this.posX) / par7;
    201            double var11 = (this.waypointY - this.posY) / par7;
    202            double var13 = (this.waypointZ - this.posZ) / par7;
    203            AxisAlignedBB var15 = this.boundingBox.copy();
    204
    205            for (int var16 = 1; (double)var16 < par7; ++var16)
    206            {
    207                var15.offset(var9, var11, var13);
    208
    209                if (!this.worldObj.getCollidingBoundingBoxes(this, var15).isEmpty())
    210                {
    211                    return false;
    212                }
    213            }
    214
    215            return true;
    216        }
    217
    218        /**
    219         * Returns the sound this mob makes while it's alive.
    220         */
    221        protected String getLivingSound()
    222        {
    223            return "mob.ghast.moan";
    224        }
    225
    226        /**
    227         * Returns the sound this mob makes when it is hurt.
    228         */
    229        protected String getHurtSound()
    230        {
    231            return "mob.ghast.scream";
    232        }
    233
    234        /**
    235         * Returns the sound this mob makes on death.
    236         */
    237        protected String getDeathSound()
    238        {
    239            return "mob.ghast.death";
    240        }
    241
    242        /**
    243         * Returns the item ID for the item the mob drops on death.
    244         */
    245        protected int getDropItemId()
    246        {
    247            return Item.gunpowder.shiftedIndex;
    248        }
    249
    250        /**
    251         * Drop 0-2 items of this living's type
    252         */
    253        protected void dropFewItems(boolean par1, int par2)
    254        {
    255            int var3 = this.rand.nextInt(2) + this.rand.nextInt(1 + par2);
    256            int var4;
    257
    258            for (var4 = 0; var4 < var3; ++var4)
    259            {
    260                this.dropItem(Item.ghastTear.shiftedIndex, 1);
    261            }
    262
    263            var3 = this.rand.nextInt(3) + this.rand.nextInt(1 + par2);
    264
    265            for (var4 = 0; var4 < var3; ++var4)
    266            {
    267                this.dropItem(Item.gunpowder.shiftedIndex, 1);
    268            }
    269        }
    270
    271        /**
    272         * Returns the volume for the sounds this mob makes.
    273         */
    274        protected float getSoundVolume()
    275        {
    276            return 10.0F;
    277        }
    278
    279        /**
    280         * Checks if the entity's current position is a valid location to spawn this entity.
    281         */
    282        public boolean getCanSpawnHere()
    283        {
    284            return this.rand.nextInt(20) == 0 && super.getCanSpawnHere() && this.worldObj.difficultySetting > 0;
    285        }
    286
    287        /**
    288         * Will return how many at most can spawn in a chunk at once.
    289         */
    290        public int getMaxSpawnedInChunk()
    291        {
    292            return 1;
    293        }
    294
    295        /**
    296         * (abstract) Protected helper method to write subclass entity data to NBT.
    297         */
    298        public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    299        {
    300            super.writeEntityToNBT(par1NBTTagCompound);
    301            par1NBTTagCompound.setInteger("ExplosionPower", this.field_92009_j);
    302        }
    303
    304        /**
    305         * (abstract) Protected helper method to read subclass entity data from NBT.
    306         */
    307        public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    308        {
    309            super.readEntityFromNBT(par1NBTTagCompound);
    310
    311            if (par1NBTTagCompound.hasKey("ExplosionPower"))
    312            {
    313                this.field_92009_j = par1NBTTagCompound.getInteger("ExplosionPower");
    314            }
    315        }
    316    }