001 package net.minecraft.tileentity; 002 003 import cpw.mods.fml.relauncher.Side; 004 import cpw.mods.fml.relauncher.SideOnly; 005 import java.util.ArrayList; 006 import java.util.Iterator; 007 import java.util.List; 008 import net.minecraft.entity.Entity; 009 import net.minecraft.entity.EntityList; 010 import net.minecraft.entity.EntityLiving; 011 import net.minecraft.nbt.NBTBase; 012 import net.minecraft.nbt.NBTTagCompound; 013 import net.minecraft.nbt.NBTTagList; 014 import net.minecraft.network.packet.Packet; 015 import net.minecraft.network.packet.Packet132TileEntityData; 016 import net.minecraft.util.AxisAlignedBB; 017 import net.minecraft.util.WeightedRandom; 018 import net.minecraft.world.World; 019 020 public class TileEntityMobSpawner extends TileEntity 021 { 022 /** The stored delay before a new spawn. */ 023 public int delay = -1; 024 025 /** 026 * The string ID of the mobs being spawned from this spawner. Defaults to pig, apparently. 027 */ 028 private String mobID = "Pig"; 029 private List field_92016_e = null; 030 031 /** The extra NBT data to add to spawned entities */ 032 private TileEntityMobSpawnerSpawnData spawnerTags = null; 033 public double yaw; 034 public double yaw2 = 0.0D; 035 private int minSpawnDelay = 200; 036 private int maxSpawnDelay = 800; 037 private int spawnCount = 4; 038 private Entity field_92017_j; 039 040 /** Maximum number of entities for limiting mob spawning */ 041 private int maxNearbyEntities = 6; 042 043 /** Required player range for mob spawning to occur */ 044 private int requiredPlayerRange = 16; 045 046 /** Range for spawning new entities with mob spawners */ 047 private int spawnRange = 4; 048 049 public TileEntityMobSpawner() 050 { 051 this.delay = 20; 052 } 053 054 public String func_92015_a() 055 { 056 return this.spawnerTags == null ? this.mobID : this.spawnerTags.field_92033_c; 057 } 058 059 public void setMobID(String par1Str) 060 { 061 this.mobID = par1Str; 062 } 063 064 /** 065 * Returns true if there is a player in range (using World.getClosestPlayer) 066 */ 067 public boolean anyPlayerInRange() 068 { 069 return this.worldObj.getClosestPlayer((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D, (double)this.requiredPlayerRange) != null; 070 } 071 072 /** 073 * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count 074 * ticks and creates a new spawn inside its implementation. 075 */ 076 public void updateEntity() 077 { 078 if (this.anyPlayerInRange()) 079 { 080 double var5; 081 082 if (this.worldObj.isRemote) 083 { 084 double var1 = (double)((float)this.xCoord + this.worldObj.rand.nextFloat()); 085 double var3 = (double)((float)this.yCoord + this.worldObj.rand.nextFloat()); 086 var5 = (double)((float)this.zCoord + this.worldObj.rand.nextFloat()); 087 this.worldObj.spawnParticle("smoke", var1, var3, var5, 0.0D, 0.0D, 0.0D); 088 this.worldObj.spawnParticle("flame", var1, var3, var5, 0.0D, 0.0D, 0.0D); 089 090 if (this.delay > 0) 091 { 092 --this.delay; 093 } 094 095 this.yaw2 = this.yaw; 096 this.yaw = (this.yaw + (double)(1000.0F / ((float)this.delay + 200.0F))) % 360.0D; 097 } 098 else 099 { 100 if (this.delay == -1) 101 { 102 this.updateDelay(); 103 } 104 105 if (this.delay > 0) 106 { 107 --this.delay; 108 return; 109 } 110 111 boolean var12 = false; 112 113 for (int var2 = 0; var2 < this.spawnCount; ++var2) 114 { 115 Entity var13 = EntityList.createEntityByName(this.func_92015_a(), this.worldObj); 116 117 if (var13 == null) 118 { 119 return; 120 } 121 122 int var4 = this.worldObj.getEntitiesWithinAABB(var13.getClass(), AxisAlignedBB.getAABBPool().addOrModifyAABBInPool((double)this.xCoord, (double)this.yCoord, (double)this.zCoord, (double)(this.xCoord + 1), (double)(this.yCoord + 1), (double)(this.zCoord + 1)).expand((double)(this.spawnRange * 2), 4.0D, (double)(this.spawnRange * 2))).size(); 123 124 if (var4 >= this.maxNearbyEntities) 125 { 126 this.updateDelay(); 127 return; 128 } 129 130 if (var13 != null) 131 { 132 var5 = (double)this.xCoord + (this.worldObj.rand.nextDouble() - this.worldObj.rand.nextDouble()) * (double)this.spawnRange; 133 double var7 = (double)(this.yCoord + this.worldObj.rand.nextInt(3) - 1); 134 double var9 = (double)this.zCoord + (this.worldObj.rand.nextDouble() - this.worldObj.rand.nextDouble()) * (double)this.spawnRange; 135 EntityLiving var11 = var13 instanceof EntityLiving ? (EntityLiving)var13 : null; 136 var13.setLocationAndAngles(var5, var7, var9, this.worldObj.rand.nextFloat() * 360.0F, 0.0F); 137 138 if (var11 == null || var11.getCanSpawnHere()) 139 { 140 this.writeNBTTagsTo(var13); 141 this.worldObj.spawnEntityInWorld(var13); 142 this.worldObj.playAuxSFX(2004, this.xCoord, this.yCoord, this.zCoord, 0); 143 144 if (var11 != null) 145 { 146 var11.spawnExplosionParticle(); 147 } 148 149 var12 = true; 150 } 151 } 152 } 153 154 if (var12) 155 { 156 this.updateDelay(); 157 } 158 } 159 160 super.updateEntity(); 161 } 162 } 163 164 public void writeNBTTagsTo(Entity par1Entity) 165 { 166 if (this.spawnerTags != null) 167 { 168 NBTTagCompound var2 = new NBTTagCompound(); 169 par1Entity.addEntityID(var2); 170 Iterator var3 = this.spawnerTags.field_92032_b.getTags().iterator(); 171 172 while (var3.hasNext()) 173 { 174 NBTBase var4 = (NBTBase)var3.next(); 175 var2.setTag(var4.getName(), var4.copy()); 176 } 177 178 par1Entity.readFromNBT(var2); 179 } 180 else if (par1Entity instanceof EntityLiving && par1Entity.worldObj != null) 181 { 182 ((EntityLiving)par1Entity).initCreature(); 183 } 184 } 185 186 /** 187 * Sets the delay before a new spawn (base delay of 200 + random number up to 600). 188 */ 189 private void updateDelay() 190 { 191 if (this.maxSpawnDelay <= this.minSpawnDelay) 192 { 193 this.delay = this.minSpawnDelay; 194 } 195 else 196 { 197 this.delay = this.minSpawnDelay + this.worldObj.rand.nextInt(this.maxSpawnDelay - this.minSpawnDelay); 198 } 199 200 if (this.field_92016_e != null && this.field_92016_e.size() > 0) 201 { 202 this.spawnerTags = (TileEntityMobSpawnerSpawnData)WeightedRandom.getRandomItem(this.worldObj.rand, this.field_92016_e); 203 this.worldObj.markBlockForUpdate(this.xCoord, this.yCoord, this.zCoord); 204 } 205 206 this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, 1, 0); 207 } 208 209 /** 210 * Reads a tile entity from NBT. 211 */ 212 public void readFromNBT(NBTTagCompound par1NBTTagCompound) 213 { 214 super.readFromNBT(par1NBTTagCompound); 215 this.mobID = par1NBTTagCompound.getString("EntityId"); 216 this.delay = par1NBTTagCompound.getShort("Delay"); 217 218 if (par1NBTTagCompound.hasKey("SpawnPotentials")) 219 { 220 this.field_92016_e = new ArrayList(); 221 NBTTagList var2 = par1NBTTagCompound.getTagList("SpawnPotentials"); 222 223 for (int var3 = 0; var3 < var2.tagCount(); ++var3) 224 { 225 this.field_92016_e.add(new TileEntityMobSpawnerSpawnData(this, (NBTTagCompound)var2.tagAt(var3))); 226 } 227 } 228 else 229 { 230 this.field_92016_e = null; 231 } 232 233 if (par1NBTTagCompound.hasKey("SpawnData")) 234 { 235 this.spawnerTags = new TileEntityMobSpawnerSpawnData(this, par1NBTTagCompound.getCompoundTag("SpawnData"), this.mobID); 236 } 237 else 238 { 239 this.spawnerTags = null; 240 } 241 242 if (par1NBTTagCompound.hasKey("MinSpawnDelay")) 243 { 244 this.minSpawnDelay = par1NBTTagCompound.getShort("MinSpawnDelay"); 245 this.maxSpawnDelay = par1NBTTagCompound.getShort("MaxSpawnDelay"); 246 this.spawnCount = par1NBTTagCompound.getShort("SpawnCount"); 247 } 248 249 if (par1NBTTagCompound.hasKey("MaxNearbyEntities")) 250 { 251 this.maxNearbyEntities = par1NBTTagCompound.getShort("MaxNearbyEntities"); 252 this.requiredPlayerRange = par1NBTTagCompound.getShort("RequiredPlayerRange"); 253 } 254 255 if (par1NBTTagCompound.hasKey("SpawnRange")) 256 { 257 this.spawnRange = par1NBTTagCompound.getShort("SpawnRange"); 258 } 259 260 if (this.worldObj != null && this.worldObj.isRemote) 261 { 262 this.field_92017_j = null; 263 } 264 } 265 266 /** 267 * Writes a tile entity to NBT. 268 */ 269 public void writeToNBT(NBTTagCompound par1NBTTagCompound) 270 { 271 super.writeToNBT(par1NBTTagCompound); 272 par1NBTTagCompound.setString("EntityId", this.func_92015_a()); 273 par1NBTTagCompound.setShort("Delay", (short)this.delay); 274 par1NBTTagCompound.setShort("MinSpawnDelay", (short)this.minSpawnDelay); 275 par1NBTTagCompound.setShort("MaxSpawnDelay", (short)this.maxSpawnDelay); 276 par1NBTTagCompound.setShort("SpawnCount", (short)this.spawnCount); 277 par1NBTTagCompound.setShort("MaxNearbyEntities", (short)this.maxNearbyEntities); 278 par1NBTTagCompound.setShort("RequiredPlayerRange", (short)this.requiredPlayerRange); 279 par1NBTTagCompound.setShort("SpawnRange", (short)this.spawnRange); 280 281 if (this.spawnerTags != null) 282 { 283 par1NBTTagCompound.setCompoundTag("SpawnData", (NBTTagCompound)this.spawnerTags.field_92032_b.copy()); 284 } 285 286 if (this.spawnerTags != null || this.field_92016_e != null && this.field_92016_e.size() > 0) 287 { 288 NBTTagList var2 = new NBTTagList(); 289 290 if (this.field_92016_e != null && this.field_92016_e.size() > 0) 291 { 292 Iterator var3 = this.field_92016_e.iterator(); 293 294 while (var3.hasNext()) 295 { 296 TileEntityMobSpawnerSpawnData var4 = (TileEntityMobSpawnerSpawnData)var3.next(); 297 var2.appendTag(var4.func_92030_a()); 298 } 299 } 300 else 301 { 302 var2.appendTag(this.spawnerTags.func_92030_a()); 303 } 304 305 par1NBTTagCompound.setTag("SpawnPotentials", var2); 306 } 307 } 308 309 @SideOnly(Side.CLIENT) 310 311 /** 312 * will create the entity from the internalID the first time it is accessed 313 */ 314 public Entity getMobEntity() 315 { 316 if (this.field_92017_j == null) 317 { 318 Entity var1 = EntityList.createEntityByName(this.func_92015_a(), (World)null); 319 this.writeNBTTagsTo(var1); 320 this.field_92017_j = var1; 321 } 322 323 return this.field_92017_j; 324 } 325 326 /** 327 * Overriden in a sign to provide the text. 328 */ 329 public Packet getDescriptionPacket() 330 { 331 NBTTagCompound var1 = new NBTTagCompound(); 332 this.writeToNBT(var1); 333 var1.removeTag("SpawnPotentials"); 334 return new Packet132TileEntityData(this.xCoord, this.yCoord, this.zCoord, 1, var1); 335 } 336 337 /** 338 * Called when a client event is received with the event number and argument, see World.sendClientEvent 339 */ 340 public void receiveClientEvent(int par1, int par2) 341 { 342 if (par1 == 1 && this.worldObj.isRemote) 343 { 344 this.delay = this.minSpawnDelay; 345 } 346 } 347 }