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 * Returns the sound this mob makes while it's alive.
037 */
038 protected String getLivingSound()
039 {
040 return "mob.bat.idle";
041 }
042
043 /**
044 * Returns the sound this mob makes when it is hurt.
045 */
046 protected String getHurtSound()
047 {
048 return "mob.bat.hurt";
049 }
050
051 /**
052 * Returns the sound this mob makes on death.
053 */
054 protected String getDeathSound()
055 {
056 return "mob.bat.death";
057 }
058
059 public int getMaxHealth()
060 {
061 return 6;
062 }
063
064 public boolean getIsBatHanging()
065 {
066 return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0;
067 }
068
069 public void setIsBatHanging(boolean par1)
070 {
071 byte var2 = this.dataWatcher.getWatchableObjectByte(16);
072
073 if (par1)
074 {
075 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 1)));
076 }
077 else
078 {
079 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -2)));
080 }
081 }
082
083 /**
084 * Returns true if the newer Entity AI code should be run
085 */
086 protected boolean isAIEnabled()
087 {
088 return true;
089 }
090
091 /**
092 * Called to update the entity's position/logic.
093 */
094 public void onUpdate()
095 {
096 super.onUpdate();
097
098 if (this.getIsBatHanging())
099 {
100 this.motionX = this.motionY = this.motionZ = 0.0D;
101 this.posY = (double)MathHelper.floor_double(this.posY) + 1.0D - (double)this.height;
102 }
103 else
104 {
105 this.motionY *= 0.6000000238418579D;
106 }
107 }
108
109 protected void updateAITasks()
110 {
111 super.updateAITasks();
112
113 if (this.getIsBatHanging())
114 {
115 if (!this.worldObj.isBlockNormalCube(MathHelper.floor_double(this.posX), (int)this.posY + 1, MathHelper.floor_double(this.posZ)))
116 {
117 this.setIsBatHanging(false);
118 this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1015, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
119 }
120 else
121 {
122 if (this.rand.nextInt(200) == 0)
123 {
124 this.rotationYawHead = (float)this.rand.nextInt(360);
125 }
126
127 if (this.worldObj.getClosestPlayerToEntity(this, 4.0D) != null)
128 {
129 this.setIsBatHanging(false);
130 this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1015, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
131 }
132 }
133 }
134 else
135 {
136 if (this.currentFlightTarget != null && (!this.worldObj.isAirBlock(this.currentFlightTarget.posX, this.currentFlightTarget.posY, this.currentFlightTarget.posZ) || this.currentFlightTarget.posY < 1))
137 {
138 this.currentFlightTarget = null;
139 }
140
141 if (this.currentFlightTarget == null || this.rand.nextInt(30) == 0 || this.currentFlightTarget.getDistanceSquared((int)this.posX, (int)this.posY, (int)this.posZ) < 4.0F)
142 {
143 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));
144 }
145
146 double var1 = (double)this.currentFlightTarget.posX + 0.5D - this.posX;
147 double var3 = (double)this.currentFlightTarget.posY + 0.1D - this.posY;
148 double var5 = (double)this.currentFlightTarget.posZ + 0.5D - this.posZ;
149 this.motionX += (Math.signum(var1) * 0.5D - this.motionX) * 0.10000000149011612D;
150 this.motionY += (Math.signum(var3) * 0.699999988079071D - this.motionY) * 0.10000000149011612D;
151 this.motionZ += (Math.signum(var5) * 0.5D - this.motionZ) * 0.10000000149011612D;
152 float var7 = (float)(Math.atan2(this.motionZ, this.motionX) * 180.0D / Math.PI) - 90.0F;
153 float var8 = MathHelper.wrapAngleTo180_float(var7 - this.rotationYaw);
154 this.moveForward = 0.5F;
155 this.rotationYaw += var8;
156
157 if (this.rand.nextInt(100) == 0 && this.worldObj.isBlockNormalCube(MathHelper.floor_double(this.posX), (int)this.posY + 1, MathHelper.floor_double(this.posZ)))
158 {
159 this.setIsBatHanging(true);
160 }
161 }
162 }
163
164 /**
165 * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
166 * prevent them from trampling crops
167 */
168 protected boolean canTriggerWalking()
169 {
170 return false;
171 }
172
173 /**
174 * Called when the mob is falling. Calculates and applies fall damage.
175 */
176 protected void fall(float par1) {}
177
178 /**
179 * Takes in the distance the entity has fallen this tick and whether its on the ground to update the fall distance
180 * and deal fall damage if landing on the ground. Args: distanceFallenThisTick, onGround
181 */
182 protected void updateFallState(double par1, boolean par3) {}
183
184 /**
185 * Return whether this entity should NOT trigger a pressure plate or a tripwire.
186 */
187 public boolean doesEntityNotTriggerPressurePlate()
188 {
189 return true;
190 }
191
192 /**
193 * Called when the entity is attacked.
194 */
195 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
196 {
197 if (!this.worldObj.isRemote && this.getIsBatHanging())
198 {
199 this.setIsBatHanging(false);
200 }
201
202 return super.attackEntityFrom(par1DamageSource, par2);
203 }
204
205 /**
206 * (abstract) Protected helper method to read subclass entity data from NBT.
207 */
208 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
209 {
210 super.readEntityFromNBT(par1NBTTagCompound);
211 this.dataWatcher.updateObject(16, Byte.valueOf(par1NBTTagCompound.getByte("BatFlags")));
212 }
213
214 /**
215 * (abstract) Protected helper method to write subclass entity data to NBT.
216 */
217 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
218 {
219 super.writeEntityToNBT(par1NBTTagCompound);
220 par1NBTTagCompound.setByte("BatFlags", this.dataWatcher.getWatchableObjectByte(16));
221 }
222
223 /**
224 * Checks if the entity's current position is a valid location to spawn this entity.
225 */
226 public boolean getCanSpawnHere()
227 {
228 int var1 = MathHelper.floor_double(this.boundingBox.minY);
229
230 if (var1 >= 63)
231 {
232 return false;
233 }
234 else
235 {
236 int var2 = MathHelper.floor_double(this.posX);
237 int var3 = MathHelper.floor_double(this.posZ);
238 int var4 = this.worldObj.getBlockLightValue(var2, var1, var3);
239 byte var5 = 4;
240 Calendar var6 = this.worldObj.getCurrentDate();
241
242 if ((var6.get(2) + 1 != 10 || var6.get(5) < 20) && (var6.get(2) + 1 != 11 || var6.get(5) > 3))
243 {
244 if (this.rand.nextBoolean())
245 {
246 return false;
247 }
248 }
249 else
250 {
251 var5 = 7;
252 }
253
254 return var4 > this.rand.nextInt(var5) ? false : super.getCanSpawnHere();
255 }
256 }
257
258 /**
259 * Initialize this creature.
260 */
261 public void initCreature() {}
262 }