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    }