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