    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}