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    }