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    }