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