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