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                        EntityLargeFireball var17 = new EntityLargeFireball(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    }