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