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