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 }