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}