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 }