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