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 }