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