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