001package net.minecraft.entity.passive;
002
003import java.util.Calendar;
004import net.minecraft.entity.Entity;
005import net.minecraft.entity.player.EntityPlayer;
006import net.minecraft.nbt.NBTTagCompound;
007import net.minecraft.util.ChunkCoordinates;
008import net.minecraft.util.DamageSource;
009import net.minecraft.util.MathHelper;
010import net.minecraft.world.World;
011
012public 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 b0 = this.dataWatcher.getWatchableObjectByte(16);
099
100        if (par1)
101        {
102            this.dataWatcher.updateObject(16, Byte.valueOf((byte)(b0 | 1)));
103        }
104        else
105        {
106            this.dataWatcher.updateObject(16, Byte.valueOf((byte)(b0 & -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 d0 = (double)this.currentFlightTarget.posX + 0.5D - this.posX;
174            double d1 = (double)this.currentFlightTarget.posY + 0.1D - this.posY;
175            double d2 = (double)this.currentFlightTarget.posZ + 0.5D - this.posZ;
176            this.motionX += (Math.signum(d0) * 0.5D - this.motionX) * 0.10000000149011612D;
177            this.motionY += (Math.signum(d1) * 0.699999988079071D - this.motionY) * 0.10000000149011612D;
178            this.motionZ += (Math.signum(d2) * 0.5D - this.motionZ) * 0.10000000149011612D;
179            float f = (float)(Math.atan2(this.motionZ, this.motionX) * 180.0D / Math.PI) - 90.0F;
180            float f1 = MathHelper.wrapAngleTo180_float(f - this.rotationYaw);
181            this.moveForward = 0.5F;
182            this.rotationYaw += f1;
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 i = MathHelper.floor_double(this.boundingBox.minY);
263
264        if (i >= 63)
265        {
266            return false;
267        }
268        else
269        {
270            int j = MathHelper.floor_double(this.posX);
271            int k = MathHelper.floor_double(this.posZ);
272            int l = this.worldObj.getBlockLightValue(j, i, k);
273            byte b0 = 4;
274            Calendar calendar = this.worldObj.getCurrentDate();
275
276            if ((calendar.get(2) + 1 != 10 || calendar.get(5) < 20) && (calendar.get(2) + 1 != 11 || calendar.get(5) > 3))
277            {
278                if (this.rand.nextBoolean())
279                {
280                    return false;
281                }
282            }
283            else
284            {
285                b0 = 7;
286            }
287
288            return l > this.rand.nextInt(b0) ? false : super.getCanSpawnHere();
289        }
290    }
291
292    /**
293     * Initialize this creature.
294     */
295    public void initCreature() {}
296}