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 }