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