    001package net.minecraft.entity.monster;
    002
    003import net.minecraft.entity.EntityLiving;
    004import net.minecraft.entity.player.EntityPlayer;
    005import net.minecraft.item.Item;
    006import net.minecraft.nbt.NBTTagCompound;
    007import net.minecraft.util.DamageSource;
    008import net.minecraft.util.MathHelper;
    009import net.minecraft.world.World;
    010import net.minecraft.world.WorldType;
    011import net.minecraft.world.biome.BiomeGenBase;
    012import net.minecraft.world.chunk.Chunk;
    013
    014public class EntitySlime extends EntityLiving implements IMob
    015{
    016    private static final float[] field_100000_e = new float[] {1.0F, 0.75F, 0.5F, 0.25F, 0.0F, 0.25F, 0.5F, 0.75F};
    017    public float field_70813_a;
    018    public float field_70811_b;
    019    public float field_70812_c;
    020
    021    /** the time between each jump of the slime */
    022    private int slimeJumpDelay = 0;
    023
    024    public EntitySlime(World par1World)
    025    {
    026        super(par1World);
    027        this.texture = "/mob/slime.png";
    028        int i = 1 << this.rand.nextInt(3);
    029        this.yOffset = 0.0F;
    030        this.slimeJumpDelay = this.rand.nextInt(20) + 10;
    031        this.setSlimeSize(i);
    032    }
    033
    034    protected void entityInit()
    035    {
    036        super.entityInit();
    037        this.dataWatcher.addObject(16, new Byte((byte)1));
    038    }
    039
    040    protected void setSlimeSize(int par1)
    041    {
    042        this.dataWatcher.updateObject(16, new Byte((byte)par1));
    043        this.setSize(0.6F * (float)par1, 0.6F * (float)par1);
    044        this.setPosition(this.posX, this.posY, this.posZ);
    045        this.setEntityHealth(this.getMaxHealth());
    046        this.experienceValue = par1;
    047    }
    048
    049    public int getMaxHealth()
    050    {
    051        int i = this.getSlimeSize();
    052        return i * i;
    053    }
    054
    055    /**
    056     * Returns the size of the slime.
    057     */
    058    public int getSlimeSize()
    059    {
    060        return this.dataWatcher.getWatchableObjectByte(16);
    061    }
    062
    063    /**
    064     * (abstract) Protected helper method to write subclass entity data to NBT.
    065     */
    066    public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    067    {
    068        super.writeEntityToNBT(par1NBTTagCompound);
    069        par1NBTTagCompound.setInteger("Size", this.getSlimeSize() - 1);
    070    }
    071
    072    /**
    073     * (abstract) Protected helper method to read subclass entity data from NBT.
    074     */
    075    public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    076    {
    077        super.readEntityFromNBT(par1NBTTagCompound);
    078        this.setSlimeSize(par1NBTTagCompound.getInteger("Size") + 1);
    079    }
    080
    081    /**
    082     * Returns the name of a particle effect that may be randomly created by EntitySlime.onUpdate()
    083     */
    084    protected String getSlimeParticle()
    085    {
    086        return "slime";
    087    }
    088
    089    /**
    090     * Returns the name of the sound played when the slime jumps.
    091     */
    092    protected String getJumpSound()
    093    {
    094        return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small");
    095    }
    096
    097    /**
    098     * Called to update the entity's position/logic.
    099     */
    100    public void onUpdate()
    101    {
    102        if (!this.worldObj.isRemote && this.worldObj.difficultySetting == 0 && this.getSlimeSize() > 0)
    103        {
    104            this.isDead = true;
    105        }
    106
    107        this.field_70811_b += (this.field_70813_a - this.field_70811_b) * 0.5F;
    108        this.field_70812_c = this.field_70811_b;
    109        boolean flag = this.onGround;
    110        super.onUpdate();
    111        int i;
    112
    113        if (this.onGround && !flag)
    114        {
    115            i = this.getSlimeSize();
    116
    117            for (int j = 0; j < i * 8; ++j)
    118            {
    119                float f = this.rand.nextFloat() * (float)Math.PI * 2.0F;
    120                float f1 = this.rand.nextFloat() * 0.5F + 0.5F;
    121                float f2 = MathHelper.sin(f) * (float)i * 0.5F * f1;
    122                float f3 = MathHelper.cos(f) * (float)i * 0.5F * f1;
    123                this.worldObj.spawnParticle(this.getSlimeParticle(), this.posX + (double)f2, this.boundingBox.minY, this.posZ + (double)f3, 0.0D, 0.0D, 0.0D);
    124            }
    125
    126            if (this.makesSoundOnLand())
    127            {
    128                this.playSound(this.getJumpSound(), this.getSoundVolume(), ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F) / 0.8F);
    129            }
    130
    131            this.field_70813_a = -0.5F;
    132        }
    133        else if (!this.onGround && flag)
    134        {
    135            this.field_70813_a = 1.0F;
    136        }
    137
    138        this.func_70808_l();
    139
    140        if (this.worldObj.isRemote)
    141        {
    142            i = this.getSlimeSize();
    143            this.setSize(0.6F * (float)i, 0.6F * (float)i);
    144        }
    145    }
    146
    147    protected void updateEntityActionState()
    148    {
    149        this.despawnEntity();
    150        EntityPlayer entityplayer = this.worldObj.getClosestVulnerablePlayerToEntity(this, 16.0D);
    151
    152        if (entityplayer != null)
    153        {
    154            this.faceEntity(entityplayer, 10.0F, 20.0F);
    155        }
    156
    157        if (this.onGround && this.slimeJumpDelay-- <= 0)
    158        {
    159            this.slimeJumpDelay = this.getJumpDelay();
    160
    161            if (entityplayer != null)
    162            {
    163                this.slimeJumpDelay /= 3;
    164            }
    165
    166            this.isJumping = true;
    167
    168            if (this.makesSoundOnJump())
    169            {
    170                this.playSound(this.getJumpSound(), this.getSoundVolume(), ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F) * 0.8F);
    171            }
    172
    173            this.moveStrafing = 1.0F - this.rand.nextFloat() * 2.0F;
    174            this.moveForward = (float)(1 * this.getSlimeSize());
    175        }
    176        else
    177        {
    178            this.isJumping = false;
    179
    180            if (this.onGround)
    181            {
    182                this.moveStrafing = this.moveForward = 0.0F;
    183            }
    184        }
    185    }
    186
    187    protected void func_70808_l()
    188    {
    189        this.field_70813_a *= 0.6F;
    190    }
    191
    192    /**
    193     * Gets the amount of time the slime needs to wait between jumps.
    194     */
    195    protected int getJumpDelay()
    196    {
    197        return this.rand.nextInt(20) + 10;
    198    }
    199
    200    protected EntitySlime createInstance()
    201    {
    202        return new EntitySlime(this.worldObj);
    203    }
    204
    205    /**
    206     * Will get destroyed next tick.
    207     */
    208    public void setDead()
    209    {
    210        int i = this.getSlimeSize();
    211
    212        if (!this.worldObj.isRemote && i > 1 && this.getHealth() <= 0)
    213        {
    214            int j = 2 + this.rand.nextInt(3);
    215
    216            for (int k = 0; k < j; ++k)
    217            {
    218                float f = ((float)(k % 2) - 0.5F) * (float)i / 4.0F;
    219                float f1 = ((float)(k / 2) - 0.5F) * (float)i / 4.0F;
    220                EntitySlime entityslime = this.createInstance();
    221                entityslime.setSlimeSize(i / 2);
    222                entityslime.setLocationAndAngles(this.posX + (double)f, this.posY + 0.5D, this.posZ + (double)f1, this.rand.nextFloat() * 360.0F, 0.0F);
    223                this.worldObj.spawnEntityInWorld(entityslime);
    224            }
    225        }
    226
    227        super.setDead();
    228    }
    229
    230    /**
    231     * Called by a player entity when they collide with an entity
    232     */
    233    public void onCollideWithPlayer(EntityPlayer par1EntityPlayer)
    234    {
    235        if (this.canDamagePlayer())
    236        {
    237            int i = this.getSlimeSize();
    238
    239            if (this.canEntityBeSeen(par1EntityPlayer) && this.getDistanceSqToEntity(par1EntityPlayer) < 0.6D * (double)i * 0.6D * (double)i && par1EntityPlayer.attackEntityFrom(DamageSource.causeMobDamage(this), this.getAttackStrength()))
    240            {
    241                this.playSound("mob.attack", 1.0F, (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F);
    242            }
    243        }
    244    }
    245
    246    /**
    247     * Indicates weather the slime is able to damage the player (based upon the slime's size)
    248     */
    249    protected boolean canDamagePlayer()
    250    {
    251        return this.getSlimeSize() > 1;
    252    }
    253
    254    /**
    255     * Gets the amount of damage dealt to the player when "attacked" by the slime.
    256     */
    257    protected int getAttackStrength()
    258    {
    259        return this.getSlimeSize();
    260    }
    261
    262    /**
    263     * Returns the sound this mob makes when it is hurt.
    264     */
    265    protected String getHurtSound()
    266    {
    267        return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small");
    268    }
    269
    270    /**
    271     * Returns the sound this mob makes on death.
    272     */
    273    protected String getDeathSound()
    274    {
    275        return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small");
    276    }
    277
    278    /**
    279     * Returns the item ID for the item the mob drops on death.
    280     */
    281    protected int getDropItemId()
    282    {
    283        return this.getSlimeSize() == 1 ? Item.slimeBall.itemID : 0;
    284    }
    285
    286    /**
    287     * Checks if the entity's current position is a valid location to spawn this entity.
    288     */
    289    public boolean getCanSpawnHere()
    290    {
    291        Chunk chunk = this.worldObj.getChunkFromBlockCoords(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posZ));
    292
    293        if (this.worldObj.getWorldInfo().getTerrainType().handleSlimeSpawnReduction(rand, worldObj))
    294        {
    295            return false;
    296        }
    297        else
    298        {
    299            if (this.getSlimeSize() == 1 || this.worldObj.difficultySetting > 0)
    300            {
    301                BiomeGenBase biomegenbase = this.worldObj.getBiomeGenForCoords(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posZ));
    302
    303                if (biomegenbase == BiomeGenBase.swampland && this.posY > 50.0D && this.posY < 70.0D && this.rand.nextFloat() < 0.5F && this.rand.nextFloat() < field_100000_e[this.worldObj.getMoonPhase()] && this.worldObj.getBlockLightValue(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)) <= this.rand.nextInt(8))
    304                {
    305                    return super.getCanSpawnHere();
    306                }
    307
    308                if (this.rand.nextInt(10) == 0 && chunk.getRandomWithSeed(987234911L).nextInt(10) == 0 && this.posY < 40.0D)
    309                {
    310                    return super.getCanSpawnHere();
    311                }
    312            }
    313
    314            return false;
    315        }
    316    }
    317
    318    /**
    319     * Returns the volume for the sounds this mob makes.
    320     */
    321    protected float getSoundVolume()
    322    {
    323        return 0.4F * (float)this.getSlimeSize();
    324    }
    325
    326    /**
    327     * The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently
    328     * use in wolves.
    329     */
    330    public int getVerticalFaceSpeed()
    331    {
    332        return 0;
    333    }
    334
    335    /**
    336     * Returns true if the slime makes a sound when it jumps (based upon the slime's size)
    337     */
    338    protected boolean makesSoundOnJump()
    339    {
    340        return this.getSlimeSize() > 0;
    341    }
    342
    343    /**
    344     * Returns true if the slime makes a sound when it lands after a jump (based upon the slime's size)
    345     */
    346    protected boolean makesSoundOnLand()
    347    {
    348        return this.getSlimeSize() > 2;
    349    }
    350}