001 package net.minecraft.src;
002
003 import java.util.Calendar;
004
005 public class EntityBat extends EntityAmbientCreature
006 {
007 /**
008 * randomly selected ChunkCoordinates in a 7x6x7 box around the bat (y offset -2 to 4) towards which it will fly.
009 * upon getting close a new target will be selected
010 */
011 private ChunkCoordinates currentFlightTarget;
012
013 public EntityBat(World par1World)
014 {
015 super(par1World);
016 this.texture = "/mob/bat.png";
017 this.setSize(0.5F, 0.9F);
018 this.setIsBatHanging(true);
019 }
020
021 protected void entityInit()
022 {
023 super.entityInit();
024 this.dataWatcher.addObject(16, new Byte((byte)0));
025 }
026
027 /**
028 * Returns the volume for the sounds this mob makes.
029 */
030 protected float getSoundVolume()
031 {
032 return 0.1F;
033 }
034
035 /**
036 * Gets the pitch of living sounds in living entities.
037 */
038 protected float getSoundPitch()
039 {
040 return super.getSoundPitch() * 0.95F;
041 }
042
043 /**
044 * Returns the sound this mob makes while it's alive.
045 */
046 protected String getLivingSound()
047 {
048 return this.getIsBatHanging() && this.rand.nextInt(4) != 0 ? null : "mob.bat.idle";
049 }
050
051 /**
052 * Returns the sound this mob makes when it is hurt.
053 */
054 protected String getHurtSound()
055 {
056 return "mob.bat.hurt";
057 }
058
059 /**
060 * Returns the sound this mob makes on death.
061 */
062 protected String getDeathSound()
063 {
064 return "mob.bat.death";
065 }
066
067 /**
068 * Returns true if this entity should push and be pushed by other entities when colliding.
069 */
070 public boolean canBePushed()
071 {
072 return false;
073 }
074
075 protected void collideWithEntity(Entity par1Entity) {}
076
077 protected void func_85033_bc() {}
078
079 public int getMaxHealth()
080 {
081 return 6;
082 }
083
084 public boolean getIsBatHanging()
085 {
086 return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0;
087 }
088
089 public void setIsBatHanging(boolean par1)
090 {
091 byte var2 = this.dataWatcher.getWatchableObjectByte(16);
092
093 if (par1)
094 {
095 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 1)));
096 }
097 else
098 {
099 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -2)));
100 }
101 }
102
103 /**
104 * Returns true if the newer Entity AI code should be run
105 */
106 protected boolean isAIEnabled()
107 {
108 return true;
109 }
110
111 /**
112 * Called to update the entity's position/logic.
113 */
114 public void onUpdate()
115 {
116 super.onUpdate();
117
118 if (this.getIsBatHanging())
119 {
120 this.motionX = this.motionY = this.motionZ = 0.0D;
121 this.posY = (double)MathHelper.floor_double(this.posY) + 1.0D - (double)this.height;
122 }
123 else
124 {
125 this.motionY *= 0.6000000238418579D;
126 }
127 }
128
129 protected void updateAITasks()
130 {
131 super.updateAITasks();
132
133 if (this.getIsBatHanging())
134 {
135 if (!this.worldObj.isBlockNormalCube(MathHelper.floor_double(this.posX), (int)this.posY + 1, MathHelper.floor_double(this.posZ)))
136 {
137 this.setIsBatHanging(false);
138 this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1015, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
139 }
140 else
141 {
142 if (this.rand.nextInt(200) == 0)
143 {
144 this.rotationYawHead = (float)this.rand.nextInt(360);
145 }
146
147 if (this.worldObj.getClosestPlayerToEntity(this, 4.0D) != null)
148 {
149 this.setIsBatHanging(false);
150 this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1015, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
151 }
152 }
153 }
154 else
155 {
156 if (this.currentFlightTarget != null && (!this.worldObj.isAirBlock(this.currentFlightTarget.posX, this.currentFlightTarget.posY, this.currentFlightTarget.posZ) || this.currentFlightTarget.posY < 1))
157 {
158 this.currentFlightTarget = null;
159 }
160
161 if (this.currentFlightTarget == null || this.rand.nextInt(30) == 0 || this.currentFlightTarget.getDistanceSquared((int)this.posX, (int)this.posY, (int)this.posZ) < 4.0F)
162 {
163 this.currentFlightTarget = new ChunkCoordinates((int)this.posX + this.rand.nextInt(7) - this.rand.nextInt(7), (int)this.posY + this.rand.nextInt(6) - 2, (int)this.posZ + this.rand.nextInt(7) - this.rand.nextInt(7));
164 }
165
166 double var1 = (double)this.currentFlightTarget.posX + 0.5D - this.posX;
167 double var3 = (double)this.currentFlightTarget.posY + 0.1D - this.posY;
168 double var5 = (double)this.currentFlightTarget.posZ + 0.5D - this.posZ;
169 this.motionX += (Math.signum(var1) * 0.5D - this.motionX) * 0.10000000149011612D;
170 this.motionY += (Math.signum(var3) * 0.699999988079071D - this.motionY) * 0.10000000149011612D;
171 this.motionZ += (Math.signum(var5) * 0.5D - this.motionZ) * 0.10000000149011612D;
172 float var7 = (float)(Math.atan2(this.motionZ, this.motionX) * 180.0D / Math.PI) - 90.0F;
173 float var8 = MathHelper.wrapAngleTo180_float(var7 - this.rotationYaw);
174 this.moveForward = 0.5F;
175 this.rotationYaw += var8;
176
177 if (this.rand.nextInt(100) == 0 && this.worldObj.isBlockNormalCube(MathHelper.floor_double(this.posX), (int)this.posY + 1, MathHelper.floor_double(this.posZ)))
178 {
179 this.setIsBatHanging(true);
180 }
181 }
182 }
183
184 /**
185 * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
186 * prevent them from trampling crops
187 */
188 protected boolean canTriggerWalking()
189 {
190 return false;
191 }
192
193 /**
194 * Called when the mob is falling. Calculates and applies fall damage.
195 */
196 protected void fall(float par1) {}
197
198 /**
199 * Takes in the distance the entity has fallen this tick and whether its on the ground to update the fall distance
200 * and deal fall damage if landing on the ground. Args: distanceFallenThisTick, onGround
201 */
202 protected void updateFallState(double par1, boolean par3) {}
203
204 /**
205 * Return whether this entity should NOT trigger a pressure plate or a tripwire.
206 */
207 public boolean doesEntityNotTriggerPressurePlate()
208 {
209 return true;
210 }
211
212 /**
213 * Called when the entity is attacked.
214 */
215 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
216 {
217 if (this.func_85032_ar())
218 {
219 return false;
220 }
221 else
222 {
223 if (!this.worldObj.isRemote && this.getIsBatHanging())
224 {
225 this.setIsBatHanging(false);
226 }
227
228 return super.attackEntityFrom(par1DamageSource, par2);
229 }
230 }
231
232 /**
233 * (abstract) Protected helper method to read subclass entity data from NBT.
234 */
235 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
236 {
237 super.readEntityFromNBT(par1NBTTagCompound);
238 this.dataWatcher.updateObject(16, Byte.valueOf(par1NBTTagCompound.getByte("BatFlags")));
239 }
240
241 /**
242 * (abstract) Protected helper method to write subclass entity data to NBT.
243 */
244 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
245 {
246 super.writeEntityToNBT(par1NBTTagCompound);
247 par1NBTTagCompound.setByte("BatFlags", this.dataWatcher.getWatchableObjectByte(16));
248 }
249
250 /**
251 * Checks if the entity's current position is a valid location to spawn this entity.
252 */
253 public boolean getCanSpawnHere()
254 {
255 int var1 = MathHelper.floor_double(this.boundingBox.minY);
256
257 if (var1 >= 63)
258 {
259 return false;
260 }
261 else
262 {
263 int var2 = MathHelper.floor_double(this.posX);
264 int var3 = MathHelper.floor_double(this.posZ);
265 int var4 = this.worldObj.getBlockLightValue(var2, var1, var3);
266 byte var5 = 4;
267 Calendar var6 = this.worldObj.getCurrentDate();
268
269 if ((var6.get(2) + 1 != 10 || var6.get(5) < 20) && (var6.get(2) + 1 != 11 || var6.get(5) > 3))
270 {
271 if (this.rand.nextBoolean())
272 {
273 return false;
274 }
275 }
276 else
277 {
278 var5 = 7;
279 }
280
281 return var4 > this.rand.nextInt(var5) ? false : super.getCanSpawnHere();
282 }
283 }
284
285 /**
286 * Initialize this creature.
287 */
288 public void initCreature() {}
289 }