001    package net.minecraft.entity.monster;
002    
003    import net.minecraft.entity.EntityLiving;
004    import net.minecraft.entity.player.EntityPlayer;
005    import net.minecraft.item.Item;
006    import net.minecraft.nbt.NBTTagCompound;
007    import net.minecraft.util.DamageSource;
008    import net.minecraft.util.MathHelper;
009    import net.minecraft.world.World;
010    import net.minecraft.world.WorldType;
011    import net.minecraft.world.biome.BiomeGenBase;
012    import net.minecraft.world.chunk.Chunk;
013    
014    public 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    }