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    }