    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}