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}