001package net.minecraft.tileentity;
002
003import cpw.mods.fml.relauncher.Side;
004import cpw.mods.fml.relauncher.SideOnly;
005import java.util.ArrayList;
006import java.util.Iterator;
007import java.util.List;
008import net.minecraft.entity.Entity;
009import net.minecraft.entity.EntityList;
010import net.minecraft.entity.EntityLiving;
011import net.minecraft.nbt.NBTBase;
012import net.minecraft.nbt.NBTTagCompound;
013import net.minecraft.nbt.NBTTagList;
014import net.minecraft.network.packet.Packet;
015import net.minecraft.network.packet.Packet132TileEntityData;
016import net.minecraft.util.AxisAlignedBB;
017import net.minecraft.util.WeightedRandom;
018import net.minecraft.world.World;
019
020public 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_92060_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 spawnedMob;
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 getMobID()
055    {
056        return this.spawnerTags == null ? this.mobID : this.spawnerTags.field_92084_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.getMobID(), 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_92083_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_92060_e != null && this.field_92060_e.size() > 0)
201        {
202            this.spawnerTags = (TileEntityMobSpawnerSpawnData)WeightedRandom.getRandomItem(this.worldObj.rand, this.field_92060_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_92060_e = new ArrayList();
221            NBTTagList var2 = par1NBTTagCompound.getTagList("SpawnPotentials");
222
223            for (int var3 = 0; var3 < var2.tagCount(); ++var3)
224            {
225                this.field_92060_e.add(new TileEntityMobSpawnerSpawnData(this, (NBTTagCompound)var2.tagAt(var3)));
226            }
227        }
228        else
229        {
230            this.field_92060_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.spawnedMob = 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.getMobID());
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_92083_b.copy());
284        }
285
286        if (this.spawnerTags != null || this.field_92060_e != null && this.field_92060_e.size() > 0)
287        {
288            NBTTagList var2 = new NBTTagList();
289
290            if (this.field_92060_e != null && this.field_92060_e.size() > 0)
291            {
292                Iterator var3 = this.field_92060_e.iterator();
293
294                while (var3.hasNext())
295                {
296                    TileEntityMobSpawnerSpawnData var4 = (TileEntityMobSpawnerSpawnData)var3.next();
297                    var2.appendTag(var4.func_92081_a());
298                }
299            }
300            else
301            {
302                var2.appendTag(this.spawnerTags.func_92081_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.spawnedMob == null)
317        {
318            Entity var1 = EntityList.createEntityByName(this.getMobID(), (World)null);
319            this.writeNBTTagsTo(var1);
320            this.spawnedMob = var1;
321        }
322
323        return this.spawnedMob;
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}