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}