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 /** Chances for slimes to spawn in swamps for every moon phase. */ 017 private static final float[] spawnChances = new float[] {1.0F, 0.75F, 0.5F, 0.25F, 0.0F, 0.25F, 0.5F, 0.75F}; 018 public float field_70813_a; 019 public float field_70811_b; 020 public float field_70812_c; 021 022 /** the time between each jump of the slime */ 023 private int slimeJumpDelay = 0; 024 025 public EntitySlime(World par1World) 026 { 027 super(par1World); 028 this.texture = "/mob/slime.png"; 029 int i = 1 << this.rand.nextInt(3); 030 this.yOffset = 0.0F; 031 this.slimeJumpDelay = this.rand.nextInt(20) + 10; 032 this.setSlimeSize(i); 033 } 034 035 protected void entityInit() 036 { 037 super.entityInit(); 038 this.dataWatcher.addObject(16, new Byte((byte)1)); 039 } 040 041 protected void setSlimeSize(int par1) 042 { 043 this.dataWatcher.updateObject(16, new Byte((byte)par1)); 044 this.setSize(0.6F * (float)par1, 0.6F * (float)par1); 045 this.setPosition(this.posX, this.posY, this.posZ); 046 this.setEntityHealth(this.getMaxHealth()); 047 this.experienceValue = par1; 048 } 049 050 public int getMaxHealth() 051 { 052 int i = this.getSlimeSize(); 053 return i * i; 054 } 055 056 /** 057 * Returns the size of the slime. 058 */ 059 public int getSlimeSize() 060 { 061 return this.dataWatcher.getWatchableObjectByte(16); 062 } 063 064 /** 065 * (abstract) Protected helper method to write subclass entity data to NBT. 066 */ 067 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 068 { 069 super.writeEntityToNBT(par1NBTTagCompound); 070 par1NBTTagCompound.setInteger("Size", this.getSlimeSize() - 1); 071 } 072 073 /** 074 * (abstract) Protected helper method to read subclass entity data from NBT. 075 */ 076 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 077 { 078 super.readEntityFromNBT(par1NBTTagCompound); 079 this.setSlimeSize(par1NBTTagCompound.getInteger("Size") + 1); 080 } 081 082 /** 083 * Returns the name of a particle effect that may be randomly created by EntitySlime.onUpdate() 084 */ 085 protected String getSlimeParticle() 086 { 087 return "slime"; 088 } 089 090 /** 091 * Returns the name of the sound played when the slime jumps. 092 */ 093 protected String getJumpSound() 094 { 095 return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small"); 096 } 097 098 /** 099 * Called to update the entity's position/logic. 100 */ 101 public void onUpdate() 102 { 103 if (!this.worldObj.isRemote && this.worldObj.difficultySetting == 0 && this.getSlimeSize() > 0) 104 { 105 this.isDead = true; 106 } 107 108 this.field_70811_b += (this.field_70813_a - this.field_70811_b) * 0.5F; 109 this.field_70812_c = this.field_70811_b; 110 boolean flag = this.onGround; 111 super.onUpdate(); 112 int i; 113 114 if (this.onGround && !flag) 115 { 116 i = this.getSlimeSize(); 117 118 for (int j = 0; j < i * 8; ++j) 119 { 120 float f = this.rand.nextFloat() * (float)Math.PI * 2.0F; 121 float f1 = this.rand.nextFloat() * 0.5F + 0.5F; 122 float f2 = MathHelper.sin(f) * (float)i * 0.5F * f1; 123 float f3 = MathHelper.cos(f) * (float)i * 0.5F * f1; 124 this.worldObj.spawnParticle(this.getSlimeParticle(), this.posX + (double)f2, this.boundingBox.minY, this.posZ + (double)f3, 0.0D, 0.0D, 0.0D); 125 } 126 127 if (this.makesSoundOnLand()) 128 { 129 this.playSound(this.getJumpSound(), this.getSoundVolume(), ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F) / 0.8F); 130 } 131 132 this.field_70813_a = -0.5F; 133 } 134 else if (!this.onGround && flag) 135 { 136 this.field_70813_a = 1.0F; 137 } 138 139 this.func_70808_l(); 140 141 if (this.worldObj.isRemote) 142 { 143 i = this.getSlimeSize(); 144 this.setSize(0.6F * (float)i, 0.6F * (float)i); 145 } 146 } 147 148 protected void updateEntityActionState() 149 { 150 this.despawnEntity(); 151 EntityPlayer entityplayer = this.worldObj.getClosestVulnerablePlayerToEntity(this, 16.0D); 152 153 if (entityplayer != null) 154 { 155 this.faceEntity(entityplayer, 10.0F, 20.0F); 156 } 157 158 if (this.onGround && this.slimeJumpDelay-- <= 0) 159 { 160 this.slimeJumpDelay = this.getJumpDelay(); 161 162 if (entityplayer != null) 163 { 164 this.slimeJumpDelay /= 3; 165 } 166 167 this.isJumping = true; 168 169 if (this.makesSoundOnJump()) 170 { 171 this.playSound(this.getJumpSound(), this.getSoundVolume(), ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F) * 0.8F); 172 } 173 174 this.moveStrafing = 1.0F - this.rand.nextFloat() * 2.0F; 175 this.moveForward = (float)(1 * this.getSlimeSize()); 176 } 177 else 178 { 179 this.isJumping = false; 180 181 if (this.onGround) 182 { 183 this.moveStrafing = this.moveForward = 0.0F; 184 } 185 } 186 } 187 188 protected void func_70808_l() 189 { 190 this.field_70813_a *= 0.6F; 191 } 192 193 /** 194 * Gets the amount of time the slime needs to wait between jumps. 195 */ 196 protected int getJumpDelay() 197 { 198 return this.rand.nextInt(20) + 10; 199 } 200 201 protected EntitySlime createInstance() 202 { 203 return new EntitySlime(this.worldObj); 204 } 205 206 /** 207 * Will get destroyed next tick. 208 */ 209 public void setDead() 210 { 211 int i = this.getSlimeSize(); 212 213 if (!this.worldObj.isRemote && i > 1 && this.getHealth() <= 0) 214 { 215 int j = 2 + this.rand.nextInt(3); 216 217 for (int k = 0; k < j; ++k) 218 { 219 float f = ((float)(k % 2) - 0.5F) * (float)i / 4.0F; 220 float f1 = ((float)(k / 2) - 0.5F) * (float)i / 4.0F; 221 EntitySlime entityslime = this.createInstance(); 222 entityslime.setSlimeSize(i / 2); 223 entityslime.setLocationAndAngles(this.posX + (double)f, this.posY + 0.5D, this.posZ + (double)f1, this.rand.nextFloat() * 360.0F, 0.0F); 224 this.worldObj.spawnEntityInWorld(entityslime); 225 } 226 } 227 228 super.setDead(); 229 } 230 231 /** 232 * Called by a player entity when they collide with an entity 233 */ 234 public void onCollideWithPlayer(EntityPlayer par1EntityPlayer) 235 { 236 if (this.canDamagePlayer()) 237 { 238 int i = this.getSlimeSize(); 239 240 if (this.canEntityBeSeen(par1EntityPlayer) && this.getDistanceSqToEntity(par1EntityPlayer) < 0.6D * (double)i * 0.6D * (double)i && par1EntityPlayer.attackEntityFrom(DamageSource.causeMobDamage(this), this.getAttackStrength())) 241 { 242 this.playSound("mob.attack", 1.0F, (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F); 243 } 244 } 245 } 246 247 /** 248 * Indicates weather the slime is able to damage the player (based upon the slime's size) 249 */ 250 protected boolean canDamagePlayer() 251 { 252 return this.getSlimeSize() > 1; 253 } 254 255 /** 256 * Gets the amount of damage dealt to the player when "attacked" by the slime. 257 */ 258 protected int getAttackStrength() 259 { 260 return this.getSlimeSize(); 261 } 262 263 /** 264 * Returns the sound this mob makes when it is hurt. 265 */ 266 protected String getHurtSound() 267 { 268 return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small"); 269 } 270 271 /** 272 * Returns the sound this mob makes on death. 273 */ 274 protected String getDeathSound() 275 { 276 return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small"); 277 } 278 279 /** 280 * Returns the item ID for the item the mob drops on death. 281 */ 282 protected int getDropItemId() 283 { 284 return this.getSlimeSize() == 1 ? Item.slimeBall.itemID : 0; 285 } 286 287 /** 288 * Checks if the entity's current position is a valid location to spawn this entity. 289 */ 290 public boolean getCanSpawnHere() 291 { 292 Chunk chunk = this.worldObj.getChunkFromBlockCoords(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posZ)); 293 294 if (this.worldObj.getWorldInfo().getTerrainType().handleSlimeSpawnReduction(rand, worldObj)) 295 { 296 return false; 297 } 298 else 299 { 300 if (this.getSlimeSize() == 1 || this.worldObj.difficultySetting > 0) 301 { 302 BiomeGenBase biomegenbase = this.worldObj.getBiomeGenForCoords(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posZ)); 303 304 if (biomegenbase == BiomeGenBase.swampland && this.posY > 50.0D && this.posY < 70.0D && this.rand.nextFloat() < 0.5F && this.rand.nextFloat() < spawnChances[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)) 305 { 306 return super.getCanSpawnHere(); 307 } 308 309 if (this.rand.nextInt(10) == 0 && chunk.getRandomWithSeed(987234911L).nextInt(10) == 0 && this.posY < 40.0D) 310 { 311 return super.getCanSpawnHere(); 312 } 313 } 314 315 return false; 316 } 317 } 318 319 /** 320 * Returns the volume for the sounds this mob makes. 321 */ 322 protected float getSoundVolume() 323 { 324 return 0.4F * (float)this.getSlimeSize(); 325 } 326 327 /** 328 * The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently 329 * use in wolves. 330 */ 331 public int getVerticalFaceSpeed() 332 { 333 return 0; 334 } 335 336 /** 337 * Returns true if the slime makes a sound when it jumps (based upon the slime's size) 338 */ 339 protected boolean makesSoundOnJump() 340 { 341 return this.getSlimeSize() > 0; 342 } 343 344 /** 345 * Returns true if the slime makes a sound when it lands after a jump (based upon the slime's size) 346 */ 347 protected boolean makesSoundOnLand() 348 { 349 return this.getSlimeSize() > 2; 350 } 351}