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    }