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    }